wip: view recorded pages

This commit is contained in:
2025-04-18 10:17:03 -05:00
parent 666ae19efd
commit 0b1b0a72c3
8 changed files with 687 additions and 32 deletions

5
.editorconfig Normal file
View File

@@ -0,0 +1,5 @@
root = true
[*.html.tera]
indent_style = space
indent_size = 2

452
Cargo.lock generated
View File

@@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "addr2line"
@@ -181,6 +181,12 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.9.0"
@@ -196,6 +202,16 @@ dependencies = [
"generic-array",
]
[[package]]
name = "bstr"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
dependencies = [
"memchr",
"serde",
]
[[package]]
name = "bumpalo"
version = "3.17.0"
@@ -256,6 +272,28 @@ dependencies = [
"windows-link",
]
[[package]]
name = "chrono-tz"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb"
dependencies = [
"chrono",
"chrono-tz-build",
"phf",
]
[[package]]
name = "chrono-tz-build"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1"
dependencies = [
"parse-zoneinfo",
"phf",
"phf_codegen",
]
[[package]]
name = "cipher"
version = "0.4.4"
@@ -341,6 +379,40 @@ dependencies = [
"libc",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crypto-bigint"
version = "0.5.5"
@@ -490,6 +562,12 @@ dependencies = [
"syn",
]
[[package]]
name = "deunicode"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc55fe0d1f6c107595572ec8b107c0999bb1a2e0b75e37429a4fb0d6474a0e7d"
[[package]]
name = "devise"
version = "0.4.2"
@@ -516,7 +594,7 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b035a542cf7abf01f2e3c4d5a7acbaebfefe120ae4efc7bde3df98186e4b8af7"
dependencies = [
"bitflags",
"bitflags 2.9.0",
"proc-macro2",
"proc-macro2-diagnostics",
"quote",
@@ -711,6 +789,18 @@ dependencies = [
"version_check",
]
[[package]]
name = "filetime"
version = "0.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586"
dependencies = [
"cfg-if",
"libc",
"libredox",
"windows-sys 0.59.0",
]
[[package]]
name = "fnv"
version = "1.0.7"
@@ -741,6 +831,15 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "fsevent-sys"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2"
dependencies = [
"libc",
]
[[package]]
name = "futf"
version = "0.1.5"
@@ -922,6 +1021,30 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
[[package]]
name = "globset"
version = "0.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5"
dependencies = [
"aho-corasick",
"bstr",
"log",
"regex-automata 0.4.9",
"regex-syntax 0.8.5",
]
[[package]]
name = "globwalk"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757"
dependencies = [
"bitflags 2.9.0",
"ignore",
"walkdir",
]
[[package]]
name = "group"
version = "0.13.0"
@@ -1113,6 +1236,15 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "humansize"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7"
dependencies = [
"libm",
]
[[package]]
name = "hyper"
version = "0.14.32"
@@ -1380,6 +1512,22 @@ dependencies = [
"icu_properties",
]
[[package]]
name = "ignore"
version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b"
dependencies = [
"crossbeam-deque",
"globset",
"log",
"memchr",
"regex-automata 0.4.9",
"same-file",
"walkdir",
"winapi-util",
]
[[package]]
name = "indexmap"
version = "1.9.3"
@@ -1408,6 +1556,26 @@ version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb"
[[package]]
name = "inotify"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff"
dependencies = [
"bitflags 1.3.2",
"inotify-sys",
"libc",
]
[[package]]
name = "inotify-sys"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
dependencies = [
"libc",
]
[[package]]
name = "inout"
version = "0.1.4"
@@ -1481,6 +1649,26 @@ dependencies = [
"serde_json",
]
[[package]]
name = "kqueue"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c"
dependencies = [
"kqueue-sys",
"libc",
]
[[package]]
name = "kqueue-sys"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b"
dependencies = [
"bitflags 1.3.2",
"libc",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
@@ -1502,6 +1690,17 @@ version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
[[package]]
name = "libredox"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"bitflags 2.9.0",
"libc",
"redox_syscall",
]
[[package]]
name = "linux-raw-sys"
version = "0.9.3"
@@ -1678,6 +1877,18 @@ dependencies = [
"adler2",
]
[[package]]
name = "mio"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
dependencies = [
"libc",
"log",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.48.0",
]
[[package]]
name = "mio"
version = "1.0.3"
@@ -1740,6 +1951,34 @@ dependencies = [
"memchr",
]
[[package]]
name = "normpath"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8911957c4b1549ac0dc74e30db9c8b0e66ddcd6d7acc33098f4c63a64a6d7ed"
dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "notify"
version = "6.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
dependencies = [
"bitflags 2.9.0",
"crossbeam-channel",
"filetime",
"fsevent-sys",
"inotify",
"kqueue",
"libc",
"log",
"mio 0.8.11",
"walkdir",
"windows-sys 0.48.0",
]
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
@@ -1891,7 +2130,7 @@ version = "0.10.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da"
dependencies = [
"bitflags",
"bitflags 2.9.0",
"cfg-if",
"foreign-types",
"libc",
@@ -1991,6 +2230,15 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "parse-zoneinfo"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24"
dependencies = [
"regex",
]
[[package]]
name = "pear"
version = "0.2.9"
@@ -2029,6 +2277,51 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pest"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6"
dependencies = [
"memchr",
"thiserror 2.0.12",
"ucd-trie",
]
[[package]]
name = "pest_derive"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5"
dependencies = [
"pest",
"pest_generator",
]
[[package]]
name = "pest_generator"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841"
dependencies = [
"pest",
"pest_meta",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "pest_meta"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0"
dependencies = [
"once_cell",
"pest",
"sha2",
]
[[package]]
name = "phf"
version = "0.11.3"
@@ -2335,7 +2628,7 @@ version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1"
dependencies = [
"bitflags",
"bitflags 2.9.0",
]
[[package]]
@@ -2534,6 +2827,19 @@ dependencies = [
"version_check",
]
[[package]]
name = "rocket_dyn_templates"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bbab919c9e67df3f7ac6624a32ef897df4cd61c0969f4d66f3ced0534660d7a"
dependencies = [
"normpath",
"notify",
"rocket",
"tera",
"walkdir",
]
[[package]]
name = "rocket_http"
version = "0.5.1"
@@ -2608,7 +2914,7 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf"
dependencies = [
"bitflags",
"bitflags 2.9.0",
"errno",
"libc",
"linux-raw-sys",
@@ -2670,6 +2976,15 @@ version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "schannel"
version = "0.1.27"
@@ -2725,7 +3040,7 @@ version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
dependencies = [
"bitflags",
"bitflags 2.9.0",
"core-foundation",
"core-foundation-sys",
"libc",
@@ -2756,6 +3071,7 @@ dependencies = [
"rand 0.9.0",
"reqwest",
"rocket",
"rocket_dyn_templates",
"scraper",
"serde",
"thiserror 2.0.12",
@@ -2769,7 +3085,7 @@ version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd568a4c9bb598e291a08244a5c1f5a8a6650bee243b5b0f8dbb3d9cc1d87fe8"
dependencies = [
"bitflags",
"bitflags 2.9.0",
"cssparser",
"derive_more",
"fxhash",
@@ -2969,6 +3285,16 @@ dependencies = [
"autocfg",
]
[[package]]
name = "slug"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "882a80f72ee45de3cc9a5afeb2da0331d58df69e4e7d8eeb5d3c7784ae67e724"
dependencies = [
"deunicode",
"wasm-bindgen",
]
[[package]]
name = "smallvec"
version = "1.14.0"
@@ -3122,7 +3448,7 @@ version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
dependencies = [
"bitflags",
"bitflags 2.9.0",
"core-foundation",
"system-configuration-sys",
]
@@ -3161,6 +3487,28 @@ dependencies = [
"utf-8",
]
[[package]]
name = "tera"
version = "1.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab9d851b45e865f178319da0abdbfe6acbc4328759ff18dafc3a41c16b4cd2ee"
dependencies = [
"chrono",
"chrono-tz",
"globwalk",
"humansize",
"lazy_static",
"percent-encoding",
"pest",
"pest_derive",
"rand 0.8.5",
"regex",
"serde",
"serde_json",
"slug",
"unic-segment",
]
[[package]]
name = "thiserror"
version = "1.0.69"
@@ -3276,7 +3624,7 @@ dependencies = [
"backtrace",
"bytes",
"libc",
"mio",
"mio 1.0.3",
"pin-project-lite",
"signal-hook-registry",
"socket2",
@@ -3482,6 +3830,12 @@ dependencies = [
"serde",
]
[[package]]
name = "ucd-trie"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
[[package]]
name = "uncased"
version = "0.9.10"
@@ -3492,6 +3846,56 @@ dependencies = [
"version_check",
]
[[package]]
name = "unic-char-property"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221"
dependencies = [
"unic-char-range",
]
[[package]]
name = "unic-char-range"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc"
[[package]]
name = "unic-common"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc"
[[package]]
name = "unic-segment"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23"
dependencies = [
"unic-ucd-segment",
]
[[package]]
name = "unic-ucd-segment"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700"
dependencies = [
"unic-char-property",
"unic-char-range",
"unic-ucd-version",
]
[[package]]
name = "unic-ucd-version"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4"
dependencies = [
"unic-common",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
@@ -3585,6 +3989,16 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "want"
version = "0.3.1"
@@ -3738,6 +4152,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
@@ -3832,6 +4255,15 @@ dependencies = [
"windows-link",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
@@ -4050,7 +4482,7 @@ version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags",
"bitflags 2.9.0",
]
[[package]]

View File

@@ -13,6 +13,7 @@ openidconnect = { version = "4.0.0", default-features = false, features = ["reqw
rand = "0.9.0"
reqwest = { version = "0.12.15", features = ["json", "native-tls"] }
rocket = { version = "0.5.1", features = ["json", "secrets"] }
rocket_dyn_templates = { version = "0.2.0", features = ["tera"] }
serde = { version = "1.0.219", features = ["derive"] }
thiserror = "2.0.12"
tracing = "0.1.41"

View File

@@ -1,6 +1,6 @@
use std::path::Path;
use std::collections::HashMap;
use std::io::Read;
use std::path::Path;
use std::time::SystemTime;
use jsonwebtoken::{
@@ -47,9 +47,7 @@ pub struct User {
impl User {
pub fn new(id: &'static str) -> Self {
Self {
id: id.into(),
}
Self { id: id.into() }
}
/// Return the user ID
@@ -198,7 +196,9 @@ pub async fn get_oidc_client(
}
/// Load a secret from the specified path
pub fn load_secret<P: AsRef<Path>>(path: P) -> Result<Vec<u8>, std::io::Error> {
pub fn load_secret<P: AsRef<Path>>(
path: P,
) -> Result<Vec<u8>, std::io::Error> {
let mut secret = vec![];
let mut f = std::fs::File::open(path)?;
f.read_to_end(&mut secret)?;
@@ -360,6 +360,7 @@ pub async fn oidc_callback(
cookies: &CookieJar<'_>,
ctx: &State<Context>,
config: &State<Config>,
rconfig: &RocketConfig,
) -> Result<Redirect, LoginFailed> {
trace!("{:?}", params);
let state = cookies
@@ -390,7 +391,12 @@ pub async fn oidc_callback(
ctx.make_jwt(&user, config.auth.login_ttl)
.map_err(LoginError::from)?,
))
.secure(true)
.secure(rconfig.profile != RocketConfig::DEBUG_PROFILE)
.same_site(if rconfig.profile == RocketConfig::DEBUG_PROFILE {
SameSite::Lax
} else {
SameSite::Strict
})
.http_only(true)
.expires(expires)
.build(),

View File

@@ -7,6 +7,7 @@ pub mod page;
use meilisearch_sdk::client::Client as MeilisearchClient;
use rocket::fairing::{self, AdHoc};
use rocket::Rocket;
use rocket_dyn_templates::Template;
use tracing::error;
use config::Config;
@@ -99,6 +100,7 @@ pub fn rocket() -> Rocket<rocket::Build> {
"/",
rocket::routes![
page::index,
page::get_page,
auth::oidc_callback,
auth::oidc_login,
page::post_page
@@ -107,4 +109,5 @@ pub fn rocket() -> Rocket<rocket::Build> {
.attach(AdHoc::config::<Config>())
.attach(AdHoc::try_on_ignite("Initialize context", init_context))
.attach(AdHoc::try_on_ignite("Meilisearch Setup", meilisearch_setup))
.attach(Template::fairing())
}

View File

@@ -20,7 +20,10 @@ impl TryFrom<&Config> for Client {
Some(t) => Some(std::fs::read_to_string(t).map_err(Error::Token)?),
None => None,
};
Ok(Client::new(&config.meilisearch.url, token.as_deref().map(str::trim))?)
Ok(Client::new(
&config.meilisearch.url,
token.as_deref().map(str::trim),
)?)
}
}
@@ -31,24 +34,35 @@ pub async fn ensure_index(
match client.get_index(name).await {
Ok(_) => {
debug!("Meilisearch index '{}' already exists", name);
Ok(())
},
Err(MeilisearchError::Meilisearch(e))
if e.error_code == ErrorCode::IndexNotFound =>
{
info!("Creating Meilisearch index: {}", name);
if let Err(e) = create_index(client, name).await {
create_index(client, name).await.inspect_err(|e| {
error!("Failed to create index: {}", e);
Err(e)
} else {
Ok(())
}
})?;
},
Err(e) => {
error!("Failed to check index: {}", e);
Err(e)
return Err(e);
},
}
};
client
.index(name)
.set_sortable_attributes(&["timestamp", "title"])
.await
.inspect_err(|e| {
error!("Failed to set index sortable attributes: {}", e);
})?;
client
.index(name)
.set_filterable_attributes(&["id", "user_id", "timestamp"])
.await
.inspect_err(|e| {
error!("Failed to set index filterable attributes: {}", e);
})?;
Ok(())
}
async fn create_index(

View File

@@ -2,13 +2,16 @@ use chrono::{DateTime, Utc};
use html5ever::parse_document;
use html5ever::tendril::TendrilSink;
use markup5ever_rcdom::{Handle, NodeData, RcDom};
use meilisearch_sdk::documents::DocumentsQuery;
use meilisearch_sdk::errors::Error;
use meilisearch_sdk::search::{SearchResults, Selectors};
use rand::Rng;
use rocket::form::Form;
use rocket::response::Redirect;
use rocket::serde::json::Json;
use rocket::State;
use serde::Serialize;
use rocket_dyn_templates::{context, Template};
use serde::{Deserialize, Serialize};
use tracing::{debug, error, event, instrument, Level};
use crate::auth::User;
@@ -18,14 +21,16 @@ use crate::Context;
static ID_CHARSET: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyz";
/// A saved page
#[derive(Clone, Serialize)]
#[derive(Clone, Deserialize, Serialize)]
pub struct Page {
/// Unique saved page ID
id: String,
/// User ID of page owner
user_id: String,
/// Visit timestamp
timestamp: DateTime<Utc>,
/// Visit date/time
datetime: DateTime<Utc>,
/// Visit date/time (as Unix timestamp, for sorting)
timestamp: i64,
/// Page URL
url: String,
/// Page title (extracted from HTML document)
@@ -34,6 +39,21 @@ pub struct Page {
data: String,
}
/// Saved page info (no contents)
#[derive(Clone, Deserialize, Serialize)]
pub struct PageInfo {
/// Unique saved page ID
id: String,
/// User ID of page owner
user_id: String,
/// Visit date/time
datetime: DateTime<Utc>,
/// Page URL
url: String,
/// Page title (extracted from HTML document)
title: Option<String>,
}
/// Save page form
#[derive(rocket::FromForm)]
pub struct SavePageForm {
@@ -43,10 +63,70 @@ pub struct SavePageForm {
data: String,
}
#[derive(rocket::Responder)]
pub enum IndexResponse {
Success(Template),
#[response(status = 500)]
Error(Template),
}
#[rocket::get("/")]
pub async fn index(user: Option<User>) -> Result<String, Redirect> {
pub async fn index(
user: Option<User>,
ctx: &State<Context>,
config: &State<Config>,
) -> Result<IndexResponse, Redirect> {
if let Some(user) = user {
Ok(format!("Hello, {}", user.id()))
match get_page_list(&user, ctx, config).await {
Ok(r) => Ok(IndexResponse::Success(Template::render(
"index",
context! {
user: user,
pages: r.hits.into_iter().map(|r| r.result).collect::<Vec<_>>(),
total: r.estimated_total_hits,
},
))),
Err(e) => {
error!("Failed to retrieve page list: {}", e);
Ok(IndexResponse::Error(Template::render(
"error",
context! {
error: e.to_string(),
},
)))
},
}
} else {
Err(Redirect::to(rocket::uri![crate::auth::oidc_login]))
}
}
#[derive(rocket::Responder)]
pub enum PageResponse {
#[response(content_type = "html")]
Success(String),
#[response(status = 500)]
Error(Template),
}
#[rocket::get("/page/<id>")]
pub async fn get_page(
id: &str,
user: Option<User>,
ctx: &State<Context>,
config: &State<Config>,
) -> Result<Option<PageResponse>, Redirect> {
if let Some(user) = user {
match _get_page(id, &user, ctx, config).await {
Ok(Some(p)) => Ok(Some(PageResponse::Success(p.data))),
Ok(None) => Ok(None),
Err(e) => Ok(Some(PageResponse::Error(Template::render(
"error",
context! {
error: e.to_string(),
},
)))),
}
} else {
Err(Redirect::to(rocket::uri![crate::auth::oidc_login]))
}
@@ -69,6 +149,49 @@ pub async fn post_page(
}
}
async fn get_page_list(
user: &User,
ctx: &Context,
config: &Config,
) -> Result<SearchResults<PageInfo>, Error> {
let index_name = &config.meilisearch.index;
debug!(
"Searching for pages in Meilisearch index {} owned by user {}",
index_name,
user.id()
);
let index = ctx.client.index(index_name);
let documents = index
.search()
.with_attributes_to_retrieve(Selectors::Some(&[
"id", "user_id", "datetime", "title", "url",
]))
.with_filter(&format!("user_id = {}", user.id()))
.with_sort(&["timestamp:desc"])
.execute()
.await?;
Ok(documents)
}
async fn _get_page(
id: &str,
user: &User,
ctx: &Context,
config: &Config,
) -> Result<Option<Page>, Error> {
let index_name = &config.meilisearch.index;
debug!(
"Retreiving page {} from Meilisearch index {}",
id, index_name,
);
let index = ctx.client.index(index_name);
let mut results = DocumentsQuery::new(&index)
.with_filter(&format!("user_id = {} AND id = {}", user.id(), id))
.execute()
.await?;
Ok(results.results.pop())
}
/// Save the page
#[instrument(level = "info", skip(data, ctx, config))]
pub async fn save_page(
@@ -81,10 +204,12 @@ pub async fn save_page(
let index_name = &config.meilisearch.index;
debug!("Saving page in Meilisearch index {}", index_name);
let index = ctx.client.get_index(index_name).await?;
let now = Utc::now();
let doc = Page {
id: gen_id(),
user_id: user.into(),
timestamp: Utc::now(),
datetime: now,
timestamp: now.timestamp(),
url: url.into(),
title: extract_title(data),
data: data.into(),

69
templates/index.html.tera Normal file
View File

@@ -0,0 +1,69 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta charset="utf-8" />
<title>seensite</title>
<style>
table {
width: 100%;
max-width: 100%;
}
col.timestamp {
width: 1px;
}
td {
white-space: nowrap;
padding: 0.5em;
}
td.title {
padding-right: 2em;
}
td.title,
td.url {
white-space: unset;
}
td.title div,
td.url div {
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
line-clamp: 1;
overflow: hidden;
}
</style>
</head>
<body>
<h1>Welcome</h1>
<p>Displaying {{ pages | length }} of {{ total }} pages.</p>
<table>
<colgroup>
<col class="timestamp"/>
<col class="title"/>
<col class="url" />
</colgroup>
<thead>
<tr>
<th scope="col">Date</th>
<th scope="col">Title</th>
<th scope="col">URL</th>
</tr>
</thead>
<tbody>
{% for page in pages -%}
<tr>
<td class="timestamp"><a
href="/page/{{ page.id }}"
>{{ page.datetime | date(format="%x %X") }}</a
></td>
<td class="title"><div>{{ page.title }}</div></td>
<td class="url"><div><a
href="{{ page.url }}">{{ page.url }}</a
></div></td>
</tr>
{%- endfor %}
</tbody>
</body>
</html>