experimental access control
This commit is contained in:
parent
6bf0d2cdd9
commit
f06811c7d6
29 changed files with 513 additions and 249 deletions
254
Cargo.lock
generated
254
Cargo.lock
generated
|
@ -61,9 +61,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.15"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56fc6cf8dc8c4158eed8649f9b8b0ea1518eb62b544fe9490d66fa0b349eafe9"
|
||||
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
|
@ -148,9 +148,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.68"
|
||||
version = "0.1.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842"
|
||||
checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -417,9 +417,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.3.14"
|
||||
version = "4.3.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98330784c494e49850cb23b8e2afcca13587d2500b2e3f1f78ae20248059c9be"
|
||||
checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
|
@ -428,9 +428,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.3.14"
|
||||
version = "4.3.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e182eb5f2562a67dda37e2c57af64d720a9e010c5e860ed87c056586aeafa52e"
|
||||
checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
|
@ -536,9 +536,9 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
|
|||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.8"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c"
|
||||
checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
@ -743,9 +743,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.1"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
@ -801,9 +801,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
|
|||
|
||||
[[package]]
|
||||
name = "exr"
|
||||
version = "1.6.4"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "279d3efcc55e19917fff7ab3ddd6c14afb6a90881a0078465196fe2f99d08c56"
|
||||
checksum = "d1e481eb11a482815d3e9d618db8c42a93207134662873809335a92327440c18"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"flume",
|
||||
|
@ -834,12 +834,9 @@ checksum = "25c7df09945d65ea8d70b3321547ed414bbc540aad5bac6883d021b970f35b04"
|
|||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.9.0"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
|
||||
dependencies = [
|
||||
"instant",
|
||||
]
|
||||
checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
|
||||
|
||||
[[package]]
|
||||
name = "fdeflate"
|
||||
|
@ -909,7 +906,7 @@ version = "0.6.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eeb4ed9e12f43b7fa0baae3f9cdda28352770132ef2e09a23760c29cae8bd47"
|
||||
dependencies = [
|
||||
"rustix 0.38.3",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
|
@ -1080,9 +1077,9 @@ checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
|
|||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.19"
|
||||
version = "0.3.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782"
|
||||
checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
|
@ -1099,11 +1096,10 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "2.3.1"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc52e53916c08643f1b56ec082790d1e86a32e58dc5268f897f313fbae7b4872"
|
||||
checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
|
@ -1177,18 +1173,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.2.6"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
|
||||
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
|
@ -1262,9 +1249,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "http-range-header"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29"
|
||||
checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f"
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
|
@ -1286,9 +1273,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
|||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.26"
|
||||
version = "0.14.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4"
|
||||
checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
|
@ -1317,7 +1304,7 @@ dependencies = [
|
|||
"futures-util",
|
||||
"http",
|
||||
"hyper",
|
||||
"rustls 0.21.3",
|
||||
"rustls 0.21.5",
|
||||
"tokio",
|
||||
"tokio-rustls 0.24.1",
|
||||
]
|
||||
|
@ -1427,22 +1414,11 @@ dependencies = [
|
|||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
|
||||
dependencies = [
|
||||
"hermit-abi 0.3.1",
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.7.2"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f"
|
||||
checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6"
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
|
@ -1450,8 +1426,8 @@ version = "0.4.9"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
|
||||
dependencies = [
|
||||
"hermit-abi 0.3.1",
|
||||
"rustix 0.38.3",
|
||||
"hermit-abi",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
|
@ -1466,9 +1442,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.6"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
|
||||
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
|
@ -1528,9 +1504,9 @@ checksum = "0c2cdeb66e45e9f36bfad5bbdb4d2384e70936afbee843c6f6543f0c551ebb25"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.146"
|
||||
version = "0.2.147"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b"
|
||||
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
|
@ -1549,12 +1525,6 @@ version = "0.5.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.3"
|
||||
|
@ -1855,20 +1825,20 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.15.0"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
|
||||
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
|
||||
dependencies = [
|
||||
"hermit-abi 0.2.6",
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
|
@ -2038,9 +2008,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.12"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79"
|
||||
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
|
||||
|
||||
[[package]]
|
||||
name = "pbkdf2"
|
||||
|
@ -2113,18 +2083,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.1.0"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead"
|
||||
checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "1.1.0"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07"
|
||||
checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -2133,9 +2103,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.9"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
|
||||
checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
|
@ -2176,9 +2146,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.63"
|
||||
version = "1.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
|
||||
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
@ -2194,9 +2164,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.28"
|
||||
version = "1.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
|
||||
checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
@ -2331,8 +2301,8 @@ checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575"
|
|||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata 0.3.2",
|
||||
"regex-syntax 0.7.3",
|
||||
"regex-automata 0.3.3",
|
||||
"regex-syntax 0.7.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2346,13 +2316,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.3.2"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83d3daa6976cffb758ec878f108ba0e062a45b2d6ca3a2cca965338855476caf"
|
||||
checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax 0.7.3",
|
||||
"regex-syntax 0.7.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2363,9 +2333,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
|||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.7.3"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ab07dc67230e4a4718e70fd5c20055a4334b121f1f9db8fe63ef39ce9b8c846"
|
||||
checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
|
@ -2392,7 +2362,7 @@ dependencies = [
|
|||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rustls 0.21.3",
|
||||
"rustls 0.21.5",
|
||||
"rustls-pemfile",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -2448,28 +2418,14 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
|||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.37.23"
|
||||
version = "0.38.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"errno",
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys 0.3.8",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4"
|
||||
checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5"
|
||||
dependencies = [
|
||||
"bitflags 2.3.3",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.4.3",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
|
@ -2487,9 +2443,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.21.3"
|
||||
version = "0.21.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b19faa85ecb5197342b54f987b142fb3e30d0c90da40f80ef4fa9a726e6676ed"
|
||||
checksum = "79ea77c539259495ce8ca47f53e66ae0330a8819f67e23ac96ca02f50e7b7d36"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
|
@ -2508,9 +2464,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.101.1"
|
||||
version = "0.101.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15f36a6828982f422756984e47912a7a51dcbc2a197aa791158f8ca61cd8204e"
|
||||
checksum = "513722fd73ad80a71f72b61009ea1b584bcfa1483ca93949c8f290298837fa59"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
|
@ -2518,15 +2474,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.12"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06"
|
||||
checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.13"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
|
||||
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
|
||||
|
||||
[[package]]
|
||||
name = "ryu-js"
|
||||
|
@ -2551,9 +2507,9 @@ checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
|
|||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
|
@ -2590,9 +2546,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.175"
|
||||
version = "1.0.177"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d25439cd7397d044e2748a6fe2432b5e85db703d6d097bd014b3c0ad1ebff0b"
|
||||
checksum = "63ba2516aa6bf82e0b19ca8b50019d52df58455d3cf9bdaf6315225fdd0c560a"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
@ -2610,9 +2566,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.175"
|
||||
version = "1.0.177"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b23f7ade6f110613c0d63858ddb8b94c1041f550eab58a16b371bdf2c9c80ab4"
|
||||
checksum = "401797fe7833d72109fedec6bfcbe67c0eed9b99772f26eb8afd261f0abc6fd3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -2621,9 +2577,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.103"
|
||||
version = "1.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b"
|
||||
checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
|
@ -2632,10 +2588,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_path_to_error"
|
||||
version = "0.1.11"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7f05c1d5476066defcdfacce1f52fc3cae3af1d3089727100c02ae92e5abbe0"
|
||||
checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"serde",
|
||||
]
|
||||
|
||||
|
@ -2759,9 +2716,9 @@ checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c"
|
|||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.5"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f"
|
||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
|
@ -2789,9 +2746,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.10.0"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
|
@ -2976,9 +2933,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "stringprep"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1"
|
||||
checksum = "db3737bde7edce97102e0e2b15365bf7a20bfdb5f60f4f9e8d7004258a51a8da"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
|
@ -3169,15 +3126,14 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.6.0"
|
||||
version = "3.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6"
|
||||
checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"redox_syscall 0.3.5",
|
||||
"rustix 0.37.23",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
|
@ -3194,18 +3150,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.40"
|
||||
version = "1.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
|
||||
checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.40"
|
||||
version = "1.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
||||
checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -3332,7 +3288,7 @@ version = "0.24.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
|
||||
dependencies = [
|
||||
"rustls 0.21.3",
|
||||
"rustls 0.21.5",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
|
@ -3391,9 +3347,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.4.1"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8bd22a874a2d0b70452d5597b12c537331d49060824a95f49f108994f94aa4c"
|
||||
checksum = "55ae70283aba8d2a8b411c695c437fe25b8b5e44e23e780662002fc72fb47a82"
|
||||
dependencies = [
|
||||
"bitflags 2.3.3",
|
||||
"bytes",
|
||||
|
@ -3545,9 +3501,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
|
|||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.9"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
|
||||
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
|
@ -3623,9 +3579,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
|||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.4.0"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be"
|
||||
checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d"
|
||||
dependencies = [
|
||||
"getrandom 0.2.10",
|
||||
"serde",
|
||||
|
@ -3813,9 +3769,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.0"
|
||||
version = "0.48.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
|
||||
checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
|
|
|
@ -185,8 +185,8 @@ impl fuser::Filesystem for Filesystem {
|
|||
|
||||
let parents: Vec<u64> = [dir_all_name, dir_all_ref]
|
||||
.into_iter()
|
||||
.chain(vivify_tags(DirKind::Default, dir_tag_ref).into_iter())
|
||||
.chain(vivify_tags(DirKind::ShowNames, dir_tag_name).into_iter())
|
||||
.chain(vivify_tags(DirKind::Default, dir_tag_ref))
|
||||
.chain(vivify_tags(DirKind::ShowNames, dir_tag_name))
|
||||
.collect();
|
||||
inodes.create(GraphItem::Event(Box::new(event)), parents);
|
||||
}
|
||||
|
@ -199,8 +199,8 @@ impl fuser::Filesystem for Filesystem {
|
|||
|
||||
let parents: Vec<u64> = [dir_all_name, dir_all_ref]
|
||||
.into_iter()
|
||||
.chain(vivify_tags(DirKind::Default, dir_tag_ref).into_iter())
|
||||
.chain(vivify_tags(DirKind::ShowNames, dir_tag_name).into_iter())
|
||||
.chain(vivify_tags(DirKind::Default, dir_tag_ref))
|
||||
.chain(vivify_tags(DirKind::ShowNames, dir_tag_name))
|
||||
.collect();
|
||||
|
||||
let targets: Vec<&ItemRef> = event.relations.keys().collect();
|
||||
|
@ -245,8 +245,8 @@ impl fuser::Filesystem for Filesystem {
|
|||
|
||||
let to_remove: Vec<u64> = []
|
||||
.into_iter()
|
||||
.chain(inodes.read_dir(dir_tag_ref).unwrap().into_iter())
|
||||
.chain(inodes.read_dir(dir_tag_name).unwrap().into_iter())
|
||||
.chain(inodes.read_dir(dir_tag_ref).unwrap())
|
||||
.chain(inodes.read_dir(dir_tag_name).unwrap())
|
||||
.filter_map(|(ino, entry)| match &entry.item {
|
||||
GraphItem::Directory(dir) => dir.entries.is_empty().then_some(ino),
|
||||
_ => None,
|
||||
|
|
|
@ -211,7 +211,7 @@ async fn main() -> Result<(), Error> {
|
|||
} else {
|
||||
let mut refs = Vec::new();
|
||||
let mut chunks = Vec::new();
|
||||
println!("hash file chunks");
|
||||
println!("hash file chunks for {}", file.display());
|
||||
for chunk in buffer.chunks(1024 * 1024) {
|
||||
use sha2::Digest;
|
||||
let hash = {
|
||||
|
@ -304,7 +304,7 @@ async fn main() -> Result<(), Error> {
|
|||
long,
|
||||
snippets,
|
||||
} => {
|
||||
let search = client.search(&query).await?;
|
||||
let search = client.search(&query, snippets).await?;
|
||||
if long {
|
||||
if snippets {
|
||||
println!(
|
||||
|
@ -602,5 +602,5 @@ async fn main() -> Result<(), Error> {
|
|||
}
|
||||
|
||||
fn into_hashset<T: Eq + Hash>(vec: Vec<T>) -> HashSet<T> {
|
||||
HashSet::from_iter(vec.into_iter())
|
||||
HashSet::from_iter(vec)
|
||||
}
|
||||
|
|
|
@ -232,19 +232,24 @@ impl Client {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn search(&self, query: &str) -> Result<SearchResult, Error> {
|
||||
let url =
|
||||
reqwest::Url::parse_with_params(&format!("{}search", self.base_url), [("q", query)])?;
|
||||
let res: SearchResult = self
|
||||
pub async fn search(&self, query: &str, snippets: bool) -> Result<SearchResult, Error> {
|
||||
let url = reqwest::Url::parse_with_params(
|
||||
&format!("{}search", self.base_url),
|
||||
[
|
||||
("q", query),
|
||||
("sniplen", if snippets { "256" } else { "0" }),
|
||||
],
|
||||
)?;
|
||||
let res = self
|
||||
.http
|
||||
.get(url)
|
||||
.header("authorization", format!("Bearer {}", self.token))
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?
|
||||
.json()
|
||||
.await?;
|
||||
Ok(res)
|
||||
if !res.status().is_success() {
|
||||
return Err(DynError(res.text().await?))?;
|
||||
}
|
||||
Ok(res.json().await?)
|
||||
}
|
||||
|
||||
pub async fn check(&self, refs: &[ItemRef]) -> Result<HashSet<ItemRef>, Error> {
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use crate::{actor::ActorId, event::Event, item::ItemRef};
|
||||
use crate::{
|
||||
actor::ActorId,
|
||||
event::{Event, RelInfo},
|
||||
item::ItemRef,
|
||||
};
|
||||
|
||||
type Role = String;
|
||||
|
||||
|
@ -22,14 +26,14 @@ pub struct Acl {
|
|||
// from_event_type, rel_type, to_event_type
|
||||
type Permission = (String, String, String);
|
||||
type Permissions = HashSet<Permission>;
|
||||
type Relations = HashMap<ItemRef, (Event, RelInfo)>;
|
||||
|
||||
impl Acl {
|
||||
pub fn can_send(
|
||||
&self,
|
||||
user: &ActorId,
|
||||
event: &Event,
|
||||
targets: HashMap<ItemRef, &Event>,
|
||||
) -> bool {
|
||||
pub fn can_view(&self, user: &ActorId) -> bool {
|
||||
self.users.contains_key(user) || self.admins.contains(user)
|
||||
}
|
||||
|
||||
pub fn can_send(&self, user: &ActorId, event: &Event, relations: &Relations) -> bool {
|
||||
if self.admins.contains(user) {
|
||||
return true;
|
||||
}
|
||||
|
@ -52,7 +56,7 @@ impl Acl {
|
|||
}
|
||||
|
||||
for (item_ref, rel_info) in &event.relations {
|
||||
let Some(target) = targets.get(item_ref) else {
|
||||
let Some((target, _)) = relations.get(item_ref) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::acl::Acl;
|
||||
use crate::actor::{ActorId, ActorSignature};
|
||||
use crate::derived::Derived;
|
||||
use crate::item::ItemRef;
|
||||
|
@ -24,6 +25,8 @@ pub struct Event {
|
|||
pub derived: Derived,
|
||||
pub sender: ActorId,
|
||||
pub signature: ActorSignature,
|
||||
/// the timestamp at the event's creator (in miliseconds + utc)
|
||||
/// also acts as a nonce
|
||||
pub origin_ts: u64,
|
||||
}
|
||||
|
||||
|
@ -64,7 +67,7 @@ pub enum EventContent {
|
|||
#[serde(rename = "x.redact")]
|
||||
Redact(RedactEvent),
|
||||
#[serde(rename = "x.acl")]
|
||||
Acl(AclEvent),
|
||||
Acl(Acl),
|
||||
#[serde(rename = "x.annotate")]
|
||||
Annotate(AnnotateEvent),
|
||||
#[serde(rename = "x.annotate.local")]
|
||||
|
@ -109,12 +112,6 @@ pub struct FileEvent {
|
|||
pub name: Option<String>,
|
||||
}
|
||||
|
||||
// TODO: currently unused
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct AclEvent {
|
||||
pub admins: Vec<String>,
|
||||
}
|
||||
|
||||
impl EventContent {
|
||||
pub fn get_type(&self) -> &str {
|
||||
match self {
|
||||
|
|
|
@ -27,7 +27,7 @@ reqwest = { version = "0.11.18", features = ["json", "rustls-tls"], default-feat
|
|||
serde = { version = "1.0.164", features = ["derive"] }
|
||||
serde-aux = "4.2.0"
|
||||
serde_json = "1.0.97"
|
||||
sha2 = "0.10.7"
|
||||
sha2 = { version = "0.10.7", features = ["asm"] }
|
||||
sqlx = { version = "0.6.3", features = ["sqlite", "runtime-tokio-rustls", "offline"] }
|
||||
tantivy = "0.20.2"
|
||||
thiserror = "1.0.40"
|
||||
|
|
24
server/ideas.md
Normal file
24
server/ideas.md
Normal file
|
@ -0,0 +1,24 @@
|
|||
# plugins
|
||||
|
||||
```ts
|
||||
type Plugin = IndexPlugin | BlobPlugin | DerivePlugin;
|
||||
|
||||
/// a plugin for indexing
|
||||
interface IndexPlugin {
|
||||
type: "index",
|
||||
key: string,
|
||||
}
|
||||
|
||||
///
|
||||
interface BlobPlugin {
|
||||
type: "blob",
|
||||
key: string,
|
||||
endpoint: string,
|
||||
}
|
||||
|
||||
///
|
||||
interface DerivePlugin {
|
||||
type: "derive",
|
||||
key: string,
|
||||
}
|
||||
```
|
|
@ -1,7 +1,9 @@
|
|||
-- will add more stuff over time
|
||||
-- TODO: foreign key for user
|
||||
CREATE TABLE sessions (
|
||||
id TEXT PRIMARY KEY NOT NULL,
|
||||
user TEXT NOT NULL,
|
||||
level INT NOT NULL
|
||||
);
|
||||
-- TODO: proper session management
|
||||
INSERT INTO sessions VALUES ('hunter2', 2);
|
||||
INSERT INTO sessions VALUES ('hunter2', '%2zO9HaRwyyA2-AOGcLNKnoTbpnQ6q7gTv6ds_OR-djU', 2);
|
||||
|
|
|
@ -4,6 +4,7 @@ use std::collections::HashMap;
|
|||
use std::io::Cursor;
|
||||
use std::process::Stdio;
|
||||
use tokio::process::Command;
|
||||
use tracing::debug;
|
||||
use ufh::derived::DeriveMedia;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
@ -105,7 +106,7 @@ impl Ffprobe {
|
|||
}
|
||||
|
||||
pub async fn thumbnail(&self, buffer: &[u8]) -> Option<Vec<u8>> {
|
||||
println!("generate thumbnail");
|
||||
debug!("generate thumbnail");
|
||||
match self
|
||||
.streams
|
||||
.iter()
|
||||
|
@ -124,6 +125,7 @@ impl Ffprobe {
|
|||
}
|
||||
|
||||
pub async fn subtitles(&self, buffer: &[u8]) -> Option<String> {
|
||||
debug!("extract subtitles");
|
||||
let stream = self.streams.iter().find(|s| s.codec_type == "subtitle")?;
|
||||
String::from_utf8(
|
||||
ffmpeg::extract(
|
||||
|
|
|
@ -127,7 +127,7 @@ impl Deriver {
|
|||
}
|
||||
|
||||
let text = a.into_iter().flatten().collect::<Vec<_>>().join("\n");
|
||||
if text.trim().len() == 0 {
|
||||
if text.trim().is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(text)
|
||||
|
|
|
@ -21,6 +21,8 @@ pub enum Error {
|
|||
#[error("{0}")]
|
||||
Validation(&'static str),
|
||||
#[error("{0}")]
|
||||
BadRequest(String),
|
||||
#[error("{0}")]
|
||||
ItemRefParse(ufh::item::ItemRefParseError),
|
||||
#[error("{0}")]
|
||||
RecvError(tokio::sync::broadcast::error::RecvError),
|
||||
|
@ -112,6 +114,7 @@ impl Error {
|
|||
Error::ItemRefParse(_) => StatusCode::BAD_REQUEST,
|
||||
Error::RecvError(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Error::Tantivy(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Error::BadRequest(_) => StatusCode::BAD_REQUEST,
|
||||
Error::Http(status, _) => *status,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ pub async fn get_relations(db: &Sqlite, event: &Event) -> Result<Relations, Erro
|
|||
rel_events
|
||||
.into_iter()
|
||||
.map(|(item_ref, item)| {
|
||||
let rel_info = event.relations.get(&item_ref).unwrap();
|
||||
let rel_info = &event.relations[&item_ref];
|
||||
match item {
|
||||
DbItem::Event(event) => Ok((item_ref, (*event, rel_info.clone()))),
|
||||
DbItem::Blob => Err(Error::Validation(
|
||||
|
@ -156,7 +156,8 @@ pub async fn derive(
|
|||
media: deriver.get_media().await,
|
||||
tags: HashSet::new(),
|
||||
};
|
||||
Ok((derived, deriver.get_thumbnail().await))
|
||||
let thumb = deriver.get_thumbnail().await;
|
||||
Ok((derived, thumb))
|
||||
}
|
||||
|
||||
pub async fn update_search_index(
|
||||
|
|
|
@ -1,5 +1,24 @@
|
|||
#![feature(async_fn_in_trait)] // ahh yes, experimental features
|
||||
|
||||
// general purpose lints
|
||||
#![warn(
|
||||
clippy::get_unwrap,
|
||||
clippy::unnested_or_patterns,
|
||||
clippy::undocumented_unsafe_blocks,
|
||||
illegal_floating_point_literal_pattern
|
||||
)]
|
||||
|
||||
// "production quality" lints
|
||||
// #![warn(
|
||||
// clippy::dbg_macro,
|
||||
// clippy::todo,
|
||||
// clippy::unimplemented,
|
||||
// clippy::use_debug,
|
||||
// clippy::print_stdout,
|
||||
// clippy::unwrap_in_result,
|
||||
// clippy::unwrap_used,
|
||||
// )]
|
||||
|
||||
use axum::extract::Json;
|
||||
use axum::{routing, Router, Server};
|
||||
use clap::Parser;
|
||||
|
@ -21,6 +40,7 @@ mod error;
|
|||
mod items;
|
||||
mod middleware;
|
||||
mod peer;
|
||||
mod perms;
|
||||
mod routes;
|
||||
mod state;
|
||||
|
||||
|
@ -49,6 +69,8 @@ pub struct ServerState {
|
|||
// note: sharing was previously to be able to let people anonymously view blobs, which isnt needed now
|
||||
// note: do i use #alias:server.tld like matrix or something else?
|
||||
|
||||
// TODO (future): use generic database trait instead of `Sqlite`
|
||||
|
||||
// FIXME: return json for all errors (some endpoints return plain text currently)
|
||||
|
||||
#[tokio::main]
|
||||
|
|
120
server/src/perms.rs
Normal file
120
server/src/perms.rs
Normal file
|
@ -0,0 +1,120 @@
|
|||
#![allow(unused)]
|
||||
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use crate::{
|
||||
items::events::get_relations,
|
||||
state::db::{sqlite::Sqlite, Database},
|
||||
Relations,
|
||||
};
|
||||
use ufh::{
|
||||
acl::Acl,
|
||||
actor::ActorId,
|
||||
event::{Event, EventContent},
|
||||
item::ItemRef,
|
||||
};
|
||||
|
||||
// TODO: find out how to cache this
|
||||
#[async_recursion::async_recursion]
|
||||
pub async fn can_view_event(db: &Sqlite, event: &Event, user: &ActorId) -> bool {
|
||||
let relations = get_relations(db, event).await.unwrap();
|
||||
|
||||
// event is visible if user has sent the event
|
||||
if &event.sender == user {
|
||||
return true;
|
||||
}
|
||||
|
||||
// or if event relates to any visible event
|
||||
for (event, _) in relations.values() {
|
||||
if can_view_event(db, event, user).await {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// or if an acl allows a user to view an event
|
||||
get_acl(db, &event.id)
|
||||
.await
|
||||
.is_some_and(|acl| acl.can_view(user))
|
||||
}
|
||||
|
||||
// async fn is_event_visible_nonrecursive(db: &Sqlite, event: &Event, user: &ActorId) -> bool {
|
||||
// let mut events = VecDeque::from([event.clone()]);
|
||||
//
|
||||
// while let Some(next) = events.pop_back() {
|
||||
// // event is visible if user has sent the event
|
||||
// if &event.sender == user {
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// // or if an acl allows a user to view an event
|
||||
// if get_acl(db, &event.id).await.is_some_and(|acl| acl.can_view(user)) {
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// // or if event relates to any visible event
|
||||
// let relations = get_relations(db, event).await.unwrap();
|
||||
// events.extend(relations.into_values().map(|(event, _)| event));
|
||||
// }
|
||||
//
|
||||
// false
|
||||
// }
|
||||
|
||||
struct Context<'a> {
|
||||
relations: &'a Relations,
|
||||
event: &'a Event,
|
||||
user: &'a ActorId,
|
||||
}
|
||||
|
||||
pub async fn can_send_event(db: &Sqlite, event: &Event, user: &ActorId) -> bool {
|
||||
let relations = get_relations(db, event).await.unwrap();
|
||||
let ctx = Context {
|
||||
relations: &relations,
|
||||
event,
|
||||
user,
|
||||
};
|
||||
|
||||
for (rel, _) in relations.values() {
|
||||
if !is_relation_valid(db, rel, &ctx).await {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
async fn validate_relations(db: &Sqlite, event: &Event, ctx: &Context<'_>) -> bool {
|
||||
let relations = get_relations(db, event).await.unwrap();
|
||||
for (rel, _) in relations.values() {
|
||||
if !is_relation_valid(db, rel, ctx).await {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
#[async_recursion::async_recursion]
|
||||
async fn is_relation_valid(db: &Sqlite, relation: &Event, ctx: &Context<'_>) -> bool {
|
||||
// a relation is allowed if the user has sent that event
|
||||
if &relation.sender == ctx.user {
|
||||
return true;
|
||||
}
|
||||
|
||||
if let Some(acl) = get_acl(db, &relation.id).await {
|
||||
// or if an acl set on it allows it
|
||||
acl.can_send(ctx.user, ctx.event, ctx.relations)
|
||||
} else {
|
||||
// or if all of it's relations are also valid
|
||||
validate_relations(db, relation, ctx).await
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_acl(db: &Sqlite, item_ref: &ItemRef) -> Option<Acl> {
|
||||
let result = db
|
||||
.query_relations(&[("acl", "acl")], &[item_ref.clone()])
|
||||
.await
|
||||
.ok()?;
|
||||
let (_, event) = result.into_iter().last()?;
|
||||
match event.content {
|
||||
EventContent::Acl(acl) => Some(acl),
|
||||
_ => None,
|
||||
}
|
||||
}
|
|
@ -64,7 +64,7 @@ async fn search(
|
|||
.map(|doc| {
|
||||
let item = items
|
||||
.remove(&doc.item_ref)
|
||||
.expect("search results has nonexistent event");
|
||||
.expect("search results has duplicate or nonexistent event");
|
||||
match item {
|
||||
DbItem::Blob => panic!("search result returned a blob"),
|
||||
DbItem::Event(event) => SearchDoc {
|
||||
|
|
|
@ -50,7 +50,7 @@ pub async fn route(
|
|||
}
|
||||
};
|
||||
|
||||
debug!("content-disposition: {}", disposition.to_str().unwrap());
|
||||
debug!("content-disposition: {:?}", disposition);
|
||||
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::perms::can_view_event;
|
||||
use crate::state::db::Database;
|
||||
use crate::ServerState;
|
||||
use axum::extract::{Query, State};
|
||||
|
@ -43,12 +44,13 @@ impl IntoResponse for QueryResult {
|
|||
#[tracing::instrument(skip_all, fields(query))]
|
||||
pub async fn route(
|
||||
State(state): State<Arc<ServerState>>,
|
||||
_auth: Authenticate<perms::ReadOnly>,
|
||||
auth: Authenticate<perms::ReadOnly>,
|
||||
Query(params): Query<QueryParams>,
|
||||
) -> Result<QueryResult, Error> {
|
||||
tracing::Span::current().record("query", ¶ms.query);
|
||||
debug!("enumerate");
|
||||
|
||||
let user = auth.user.unwrap();
|
||||
let queries = state.queries.read().await;
|
||||
let query = queries
|
||||
.get(¶ms.query)
|
||||
|
@ -63,6 +65,21 @@ pub async fn route(
|
|||
.db
|
||||
.query_events(&query, params.limit, params.after.map(|p| p.to_string()))
|
||||
.await?;
|
||||
|
||||
use futures_util::stream::{self, StreamExt};
|
||||
|
||||
let result = crate::state::db::QueryResult {
|
||||
events: stream::iter(result.events)
|
||||
.filter_map(|event| async {
|
||||
can_view_event(&state.db, &event, &user)
|
||||
.await
|
||||
.then_some(event)
|
||||
})
|
||||
.collect()
|
||||
.await,
|
||||
..result
|
||||
};
|
||||
|
||||
let result = match (params.timeout, result.events.is_empty()) {
|
||||
(Some(timeout), true) => {
|
||||
debug!("no events, waiting for new ones...");
|
||||
|
@ -73,9 +90,17 @@ pub async fn route(
|
|||
break match recv.recv().await {
|
||||
Err(err) => Err(err.into()),
|
||||
Ok((event, relations, rowid)) => match query.matches(&event, &relations) {
|
||||
mt @ MatchType::Event => Ok((mt, event, rowid)),
|
||||
mt @ MatchType::Relation => Ok((mt, event, rowid)),
|
||||
MatchType::None => continue,
|
||||
mt @ MatchType::Event
|
||||
if can_view_event(&state.db, &event, &user).await =>
|
||||
{
|
||||
Ok((mt, event, rowid))
|
||||
}
|
||||
mt @ MatchType::Relation
|
||||
if can_view_event(&state.db, &event, &user).await =>
|
||||
{
|
||||
Ok((mt, event, rowid))
|
||||
}
|
||||
_ => continue,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -114,8 +139,13 @@ pub async fn route(
|
|||
|
||||
// relations query
|
||||
let relations = if !query.relations.is_empty() && !result.events.is_empty() {
|
||||
let relations: Vec<_> = query.relations.into_iter().collect();
|
||||
let map = state.db.query_relations(&relations, &result.events).await?;
|
||||
let relations: Vec<_> = query
|
||||
.relations
|
||||
.iter()
|
||||
.map(|(rel_type, event_type)| (rel_type.as_str(), event_type.as_str()))
|
||||
.collect();
|
||||
let ids: Vec<_> = result.events.iter().map(|ev| ev.id.clone()).collect();
|
||||
let map = state.db.query_relations(&relations, &ids).await?;
|
||||
debug!("fetched {} relations", map.len());
|
||||
Some(map)
|
||||
} else {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use super::{Error, Response};
|
||||
use crate::routes::util::get_blob;
|
||||
use crate::state::db::{Database, Thumbnail};
|
||||
use crate::ServerState;
|
||||
|
@ -10,15 +11,15 @@ use std::fmt::Display;
|
|||
use std::io::Cursor;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tracing::debug;
|
||||
use ufh::item::ItemRef;
|
||||
|
||||
use super::{Error, Response};
|
||||
|
||||
pub async fn route(
|
||||
State(state): State<Arc<ServerState>>,
|
||||
Path(item_ref): Path<ItemRef>,
|
||||
Query(params): Query<ThumbnailParams>,
|
||||
) -> Response<(TypedHeader<ContentType>, TypedHeader<CacheControl>, Bytes)> {
|
||||
debug!("get thumbnail");
|
||||
let size = ThumbnailSize::closest_to(params.width, params.height);
|
||||
let thumb = state.db.thumbnail_get(&item_ref, &size).await?;
|
||||
let cache_control = TypedHeader(
|
||||
|
@ -29,6 +30,7 @@ pub async fn route(
|
|||
let content_type = TypedHeader(ContentType::png());
|
||||
match thumb {
|
||||
Thumbnail::Raw(bytes) => {
|
||||
debug!("generate thumbnail");
|
||||
let thumb = generate_thumb(&bytes, &size)?;
|
||||
state.db.thumbnail_create(&item_ref, &size, &thumb).await?;
|
||||
Ok((content_type, cache_control, thumb))
|
||||
|
@ -41,6 +43,7 @@ pub async fn route(
|
|||
.await?
|
||||
.ok_or(Error::NotFound)?;
|
||||
let blob = get_blob(&state.items, &event, params.via.as_deref()).await?;
|
||||
debug!("generate thumbnail");
|
||||
let thumb = generate_thumb(&blob, &size)?;
|
||||
state.db.thumbnail_create(&item_ref, &size, &thumb).await?;
|
||||
Ok((content_type, cache_control, thumb))
|
||||
|
|
|
@ -4,7 +4,10 @@ use axum::{
|
|||
};
|
||||
use bytes::Bytes;
|
||||
use reqwest::StatusCode;
|
||||
use ufh::event::{Event, EventContent};
|
||||
use ufh::{
|
||||
actor::ActorId,
|
||||
event::{Event, EventContent},
|
||||
};
|
||||
|
||||
use crate::{items::Items, ServerState};
|
||||
use std::{marker::PhantomData, sync::Arc};
|
||||
|
@ -85,6 +88,7 @@ pub mod perms {
|
|||
#[derive(Debug)]
|
||||
pub struct Authenticate<T: perms::AuthLevel> {
|
||||
pub id: Option<String>,
|
||||
pub user: Option<ActorId>,
|
||||
pub level: u8,
|
||||
_lvl: PhantomData<T>,
|
||||
}
|
||||
|
@ -127,6 +131,7 @@ impl<T: AuthLevel> Authenticate<T> {
|
|||
Err(sqlx::Error::RowNotFound) if T::new().to_num() == 0 => {
|
||||
return Ok(Authenticate {
|
||||
id: None,
|
||||
user: None,
|
||||
level: 0,
|
||||
_lvl: PhantomData,
|
||||
});
|
||||
|
@ -148,8 +153,14 @@ impl<T: AuthLevel> Authenticate<T> {
|
|||
));
|
||||
}
|
||||
|
||||
let user = query
|
||||
.user
|
||||
.parse()
|
||||
.expect("user id should be validated on input");
|
||||
|
||||
Ok(Authenticate {
|
||||
id: Some(query.id),
|
||||
user: Some(user),
|
||||
level: query.level as u8,
|
||||
_lvl: PhantomData,
|
||||
})
|
||||
|
|
|
@ -59,8 +59,8 @@ pub trait Database {
|
|||
) -> Result<QueryResult, Self::Error>;
|
||||
async fn query_relations(
|
||||
&self,
|
||||
relations: &[(String, String)],
|
||||
for_events: &[Event],
|
||||
relations: &[(&str, &str)],
|
||||
for_events: &[ItemRef],
|
||||
) -> Result<HashMap<ItemRef, Event>, Self::Error>;
|
||||
|
||||
// routes::things::create has a lot of file-specific things
|
||||
|
|
|
@ -46,28 +46,6 @@ impl Sqlite {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
extern "pseudocode" fn is_event_visible(event: &Event, user: &ActorId) -> bool {
|
||||
if event.sender == user {
|
||||
return true;
|
||||
}
|
||||
|
||||
let acl = state.db.query(Query { refs: [event.id], rels: [("acl", "x.acl")] });
|
||||
if let Some(acl) = acl {
|
||||
return acl.users.contains(user) || acl.admins.contains(user);
|
||||
}
|
||||
|
||||
let acls_that_affected_event_sending = state.db.lookup_acls(&event).await?;
|
||||
for acl in acls_that_affected_event_sending {
|
||||
if acl.users.contains(user) || acl.admins.contains(user) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
// TODO: confirm that every function here is idempotent...
|
||||
// TODO (future): ...and warn when doing the same thing multiple times (to spot optimizations)
|
||||
impl Database for Sqlite {
|
||||
|
@ -278,8 +256,8 @@ impl Database for Sqlite {
|
|||
#[tracing::instrument(skip_all)]
|
||||
async fn query_relations(
|
||||
&self,
|
||||
relations: &[(String, String)],
|
||||
for_events: &[Event],
|
||||
relations: &[(&str, &str)],
|
||||
for_events: &[ItemRef],
|
||||
) -> Result<HashMap<ItemRef, Event>, Self::Error> {
|
||||
let mut builder = sqlx::QueryBuilder::new(
|
||||
"
|
||||
|
@ -296,9 +274,9 @@ impl Database for Sqlite {
|
|||
});
|
||||
builder.push(") AND events_to.ref IN (");
|
||||
let mut sep = builder.separated(",");
|
||||
for event in for_events {
|
||||
let event_id_str = event.id.to_string();
|
||||
sep.push_bind(event_id_str);
|
||||
for item_ref in for_events {
|
||||
let item_ref_str = item_ref.to_string();
|
||||
sep.push_bind(item_ref_str);
|
||||
}
|
||||
builder.push(")");
|
||||
debug!("generated sql: {}", builder.sql());
|
||||
|
@ -338,9 +316,6 @@ impl Database for Sqlite {
|
|||
}
|
||||
builder.push(")");
|
||||
let records = builder.build().fetch_all(&self.pool).await?;
|
||||
if !partial && records.len() != item_refs.len() {
|
||||
return Err(Error::NotFound);
|
||||
}
|
||||
let mut map = HashMap::with_capacity(records.len());
|
||||
for record in records {
|
||||
let item_ref_str = record.get("item_ref");
|
||||
|
@ -359,6 +334,11 @@ impl Database for Sqlite {
|
|||
};
|
||||
map.insert(item_ref, item);
|
||||
}
|
||||
let has_all = HashSet::<_>::from_iter(item_refs) == HashSet::<_>::from_iter(map.keys());
|
||||
if !partial && !has_all {
|
||||
return Err(Error::NotFound);
|
||||
}
|
||||
|
||||
Ok(map)
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,11 @@ pub struct Tantivy {
|
|||
writer: Arc<Mutex<IndexWriter>>,
|
||||
}
|
||||
|
||||
// NOTE: should snippets be supported?
|
||||
// this requires an extra copy of the text to be stored and retreived,
|
||||
// making it much more taxing on storage and performance
|
||||
// howver, they are a *very* nice search qol feature
|
||||
|
||||
impl Tantivy {
|
||||
pub async fn open(path: impl AsRef<std::path::Path>) -> Result<Tantivy, Error> {
|
||||
let _ = tokio::fs::create_dir(&path).await;
|
||||
|
@ -47,9 +52,9 @@ impl Tantivy {
|
|||
builder.add_text_field("name", TEXT | STORED);
|
||||
builder.add_text_field("body", TEXT | STORED);
|
||||
builder.add_text_field("type", STRING | STORED);
|
||||
builder.add_text_field("tag", STRING | STORED);
|
||||
builder.add_date_field("btime", STORED);
|
||||
builder.add_date_field("mtime", STORED);
|
||||
builder.add_text_field("tag", STRING);
|
||||
builder.add_date_field("btime", INDEXED);
|
||||
builder.add_date_field("mtime", INDEXED);
|
||||
builder.build()
|
||||
}
|
||||
}
|
||||
|
@ -77,14 +82,25 @@ impl Search for Tantivy {
|
|||
}
|
||||
doc.add_text(field("body"), document.text);
|
||||
doc.add_text(field("type"), document.event.content.get_type());
|
||||
// FIXME: look at the api to see how to do this more properly
|
||||
doc.add_date(
|
||||
field("mtime"),
|
||||
DateTime::from_timestamp_millis(OffsetDateTime::now_utc().unix_timestamp()),
|
||||
DateTime::from_timestamp_nanos(
|
||||
OffsetDateTime::now_utc()
|
||||
.unix_timestamp_nanos()
|
||||
.try_into()
|
||||
.expect("your timestamp is too big!"),
|
||||
),
|
||||
);
|
||||
// FIXME: don't do `as i64`
|
||||
doc.add_date(
|
||||
field("btime"),
|
||||
DateTime::from_timestamp_millis(document.event.origin_ts as i64),
|
||||
DateTime::from_timestamp_millis(
|
||||
document
|
||||
.event
|
||||
.origin_ts
|
||||
.try_into()
|
||||
.expect("your timestamp is too big!"),
|
||||
),
|
||||
);
|
||||
|
||||
let mut product = Vec::new();
|
||||
|
@ -132,22 +148,27 @@ impl Search for Tantivy {
|
|||
);
|
||||
let parsed = parser
|
||||
.parse_query(query)
|
||||
.map_err(|_| Error::Validation("invalid query"))?;
|
||||
.map_err(|err| Error::BadRequest(format!("invalid query: {}", err)))?;
|
||||
let mut snippets = tantivy::SnippetGenerator::create(&searcher, &parsed, field("body"))?;
|
||||
snippets.set_max_num_chars(sniplen);
|
||||
let collector = TopDocs::with_limit(limit).and_offset(offset);
|
||||
let mut docs = Vec::new();
|
||||
for (score, addr) in searcher.search(&parsed, &collector)? {
|
||||
let doc = searcher.doc(addr)?;
|
||||
let snippet = snippets.snippet_from_doc(&doc);
|
||||
let snippet = (!snippet.is_empty()).then(|| Snippet {
|
||||
text: snippet.fragment().to_string(),
|
||||
ranges: snippet
|
||||
.highlighted()
|
||||
.iter()
|
||||
.map(|range| (range.start, range.end))
|
||||
.collect(),
|
||||
});
|
||||
let snippet = if sniplen > 0 {
|
||||
let snippet = snippets.snippet_from_doc(&doc);
|
||||
let snippet = (!snippet.is_empty()).then(|| Snippet {
|
||||
text: snippet.fragment().to_string(),
|
||||
ranges: snippet
|
||||
.highlighted()
|
||||
.iter()
|
||||
.map(|range| (range.start, range.end))
|
||||
.collect(),
|
||||
});
|
||||
snippet
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let item_ref: ItemRef = doc
|
||||
.get_first(field("ref"))
|
||||
.expect("document doesn't have an item ref!")
|
||||
|
@ -161,6 +182,7 @@ impl Search for Tantivy {
|
|||
item_ref,
|
||||
});
|
||||
}
|
||||
debug!("got {} docs", docs.len());
|
||||
|
||||
Ok(docs)
|
||||
}
|
||||
|
|
66
spec/acl.rs
Normal file
66
spec/acl.rs
Normal file
|
@ -0,0 +1,66 @@
|
|||
/// pseudocode for validing events
|
||||
/// the 2 main functions `is_event_visible` and `is_event_sendable`
|
||||
|
||||
type Relations = HashMap<ItemRef, (Event, RelInfo)>;
|
||||
|
||||
extern "pseudocode" fn is_event_visible(event: &Event, user: &ActorId) -> bool {
|
||||
let relations = get_relations(event);
|
||||
|
||||
// event is visible if user has sent the event
|
||||
if event.sender == user {
|
||||
return true;
|
||||
}
|
||||
|
||||
// or if event relates to any visible event
|
||||
if relations.values().any(|rel| is_event_visible(rel, user)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// or if an acl allows a user to view an event
|
||||
get_acl(&event).is_some_and(|acl| acl.can_view(user))
|
||||
}
|
||||
|
||||
struct Context {
|
||||
relations: Relations,
|
||||
event: Event,
|
||||
user: ActorId,
|
||||
}
|
||||
|
||||
extern "pseudocode" fn is_event_sendable(event: &Event, user: &ActorId) -> bool {
|
||||
let relations = get_relations(event);
|
||||
let ctx = Context { relations, event, user };
|
||||
|
||||
relations
|
||||
.values()
|
||||
.all(|rel| is_relation_valid(rel, &ctx))
|
||||
}
|
||||
|
||||
fn validate_relations(event: &Event, ctx: &Context) -> bool {
|
||||
let relations = get_relations(event);
|
||||
relations
|
||||
.values()
|
||||
.all(|rel| is_relation_valid(rel, ctx))
|
||||
}
|
||||
|
||||
fn is_relation_valid(relation: &Event, ctx: &Context) -> bool {
|
||||
// a relation is allowed if the user has sent that event
|
||||
if relation.sender == ctx.user {
|
||||
return true;
|
||||
}
|
||||
|
||||
if let Some(acl) = get_acl(&relation) {
|
||||
// or if an acl set on it allows it
|
||||
acl.can_send(ctx.user, ctx.event, ctx.relations)
|
||||
} else {
|
||||
// or if all of it's relations are also valid
|
||||
validate_relations(relation, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_relations(_event: &Event) -> Relations {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_acl(_event: &Event) -> Option<Acl> {
|
||||
unimplemented!()
|
||||
}
|
|
@ -266,3 +266,9 @@ ones so far:
|
|||
- `session`: users can manage sessions
|
||||
- `search`: has full text search
|
||||
- `share`: deprecated
|
||||
|
||||
## searching
|
||||
|
||||
see <https://docs.rs/tantivy/latest/tantivy/query/struct.QueryParser.html>
|
||||
|
||||
deriving takes a lot of dependencies, which might not be desierable
|
||||
|
|
|
@ -13,7 +13,7 @@ flume = "0.10.14"
|
|||
futures-util = "0.3.28"
|
||||
serde = { version = "1.0.163", features = ["derive"] }
|
||||
serde_json = "1.0.96"
|
||||
sha2 = "0.10.6"
|
||||
sha2 = { version = "0.10.6", features = ["asm"] }
|
||||
sqlx = { version = "0.6.3", features = ["sqlite", "runtime-tokio-rustls", "offline"] }
|
||||
thiserror = "1.0.40"
|
||||
tokio = { version = "1.28.2", features = ["rt-multi-thread", "macros", "fs"] }
|
||||
|
|
1
sweep.timestamp
Normal file
1
sweep.timestamp
Normal file
|
@ -0,0 +1 @@
|
|||
{"secs_since_epoch":1690528577,"nanos_since_epoch":119672817}
|
|
@ -6,7 +6,7 @@
|
|||
export let style = "";
|
||||
export let url = api.getBlobUrl(event.id);
|
||||
</script>
|
||||
<audio title="{event.content.name}" src={url} autoplay controls/>
|
||||
<audio title="{event.content.name}" src={url} autoplay controls loop />
|
||||
<style>
|
||||
audio {
|
||||
display: block;
|
||||
|
|
|
@ -36,10 +36,14 @@
|
|||
<Audio {event} />
|
||||
{:else if info.mime?.startsWith("video/")}
|
||||
<Video {event} style="max-width: 80vw; max-height: 80vh" />
|
||||
{:else}
|
||||
{:else if info.mime?.startsWith("text/") || info.mime === "application/json"}
|
||||
<div class="text">
|
||||
<Text {event} />
|
||||
</div>
|
||||
{:else if info.mime === "application/pdf"}
|
||||
<embed src={url} type={info.mime} />
|
||||
{:else}
|
||||
<div class="text">[/// cannot view event ///]</div>
|
||||
{/if}
|
||||
</div>
|
||||
<!-- <a target="_blank" href={url} download={event.content.name}>Open in new tab</a> -->
|
||||
|
@ -89,4 +93,9 @@
|
|||
font-family: monospace;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
embed {
|
||||
height: 80vh;
|
||||
width: 80vw;
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Reference in a new issue