allow website content to be updated
This commit is contained in:
parent
a5ffb69ccd
commit
f4b45cb2f8
14 changed files with 405 additions and 38 deletions
47
Cargo.lock
generated
47
Cargo.lock
generated
|
@ -702,9 +702,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "b64-ct"
|
name = "b64-ct"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32261e5a95e234446dd31774ef4305dc164039370fe2622fd3e935a0f2730689"
|
checksum = "41dd1e07aafe656df62d40e74d4627a0cce26da43b0bec233ac1305207a5a768"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
|
@ -962,9 +962,13 @@ dependencies = [
|
||||||
"chir-rs-http-api",
|
"chir-rs-http-api",
|
||||||
"clap",
|
"clap",
|
||||||
"color-eyre",
|
"color-eyre",
|
||||||
|
"dotenvy",
|
||||||
"eyre",
|
"eyre",
|
||||||
|
"mime",
|
||||||
|
"mime_guess",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1028,10 +1032,13 @@ dependencies = [
|
||||||
"chir-rs-misc",
|
"chir-rs-misc",
|
||||||
"chrono",
|
"chrono",
|
||||||
"eyre",
|
"eyre",
|
||||||
|
"futures",
|
||||||
"mime",
|
"mime",
|
||||||
"rusty_paseto",
|
"rusty_paseto",
|
||||||
"sentry-tower",
|
"sentry-tower",
|
||||||
|
"serde",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
"tower-http 0.6.2",
|
"tower-http 0.6.2",
|
||||||
"tracing",
|
"tracing",
|
||||||
"unicode-normalization",
|
"unicode-normalization",
|
||||||
|
@ -2482,6 +2489,16 @@ version = "0.3.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mime_guess"
|
||||||
|
version = "2.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
|
||||||
|
dependencies = [
|
||||||
|
"mime",
|
||||||
|
"unicase",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "minimal-lexical"
|
name = "minimal-lexical"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
@ -3064,10 +3081,12 @@ dependencies = [
|
||||||
"sync_wrapper 1.0.2",
|
"sync_wrapper 1.0.2",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls 0.26.0",
|
"tokio-rustls 0.26.0",
|
||||||
|
"tokio-util",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"url",
|
"url",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
|
"wasm-streams",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"webpki-roots",
|
"webpki-roots",
|
||||||
"windows-registry",
|
"windows-registry",
|
||||||
|
@ -4170,12 +4189,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-util"
|
name = "tokio-util"
|
||||||
version = "0.7.12"
|
version = "0.7.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a"
|
checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
@ -4408,6 +4428,12 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicase"
|
||||||
|
version = "2.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-bidi"
|
name = "unicode-bidi"
|
||||||
version = "0.3.17"
|
version = "0.3.17"
|
||||||
|
@ -4626,6 +4652,19 @@ version = "0.2.97"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49"
|
checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-streams"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65"
|
||||||
|
dependencies = [
|
||||||
|
"futures-util",
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.74"
|
version = "0.3.74"
|
||||||
|
|
167
Cargo.nix
167
Cargo.nix
|
@ -32,7 +32,7 @@ args@{
|
||||||
ignoreLockHash,
|
ignoreLockHash,
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
nixifiedLockHash = "7dc55944ae2b0d1b65a004e3be3f5314339c3d2e023e794d54b030b197c5bf87";
|
nixifiedLockHash = "51a600afd0b9503f1748762462902306933599d46304da01c1ecdecdb6049697";
|
||||||
workspaceSrc = if args.workspaceSrc == null then ./. else args.workspaceSrc;
|
workspaceSrc = if args.workspaceSrc == null then ./. else args.workspaceSrc;
|
||||||
currentLockHash = builtins.hashFile "sha256" (workspaceSrc + /Cargo.lock);
|
currentLockHash = builtins.hashFile "sha256" (workspaceSrc + /Cargo.lock);
|
||||||
lockHashIgnored =
|
lockHashIgnored =
|
||||||
|
@ -1927,7 +1927,7 @@ else
|
||||||
inherit profileName;
|
inherit profileName;
|
||||||
}).out;
|
}).out;
|
||||||
tokio_util =
|
tokio_util =
|
||||||
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".tokio-util."0.7.12" {
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".tokio-util."0.7.13" {
|
||||||
inherit profileName;
|
inherit profileName;
|
||||||
}).out;
|
}).out;
|
||||||
};
|
};
|
||||||
|
@ -2262,15 +2262,15 @@ else
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index".b64-ct."0.1.2" =
|
"registry+https://github.com/rust-lang/crates.io-index".b64-ct."0.1.3" =
|
||||||
overridableMkRustCrate
|
overridableMkRustCrate
|
||||||
(profileName: rec {
|
(profileName: rec {
|
||||||
name = "b64-ct";
|
name = "b64-ct";
|
||||||
version = "0.1.2";
|
version = "0.1.3";
|
||||||
registry = "registry+https://github.com/rust-lang/crates.io-index";
|
registry = "registry+https://github.com/rust-lang/crates.io-index";
|
||||||
src = fetchCratesIo {
|
src = fetchCratesIo {
|
||||||
inherit name version;
|
inherit name version;
|
||||||
sha256 = "32261e5a95e234446dd31774ef4305dc164039370fe2622fd3e935a0f2730689";
|
sha256 = "41dd1e07aafe656df62d40e74d4627a0cce26da43b0bec233ac1305207a5a768";
|
||||||
};
|
};
|
||||||
features = builtins.concatLists [
|
features = builtins.concatLists [
|
||||||
[ "default" ]
|
[ "default" ]
|
||||||
|
@ -3010,10 +3010,22 @@ else
|
||||||
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".color-eyre."0.6.3" {
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".color-eyre."0.6.3" {
|
||||||
inherit profileName;
|
inherit profileName;
|
||||||
}).out;
|
}).out;
|
||||||
|
dotenvy =
|
||||||
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".dotenvy."0.15.7" {
|
||||||
|
inherit profileName;
|
||||||
|
}).out;
|
||||||
eyre =
|
eyre =
|
||||||
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".eyre."0.6.12" {
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".eyre."0.6.12" {
|
||||||
inherit profileName;
|
inherit profileName;
|
||||||
}).out;
|
}).out;
|
||||||
|
mime =
|
||||||
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".mime."0.3.17" {
|
||||||
|
inherit profileName;
|
||||||
|
}).out;
|
||||||
|
mime_guess =
|
||||||
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".mime_guess."2.0.5" {
|
||||||
|
inherit profileName;
|
||||||
|
}).out;
|
||||||
reqwest =
|
reqwest =
|
||||||
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".reqwest."0.12.9" {
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".reqwest."0.12.9" {
|
||||||
inherit profileName;
|
inherit profileName;
|
||||||
|
@ -3022,6 +3034,10 @@ else
|
||||||
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".tokio."1.42.0" {
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".tokio."1.42.0" {
|
||||||
inherit profileName;
|
inherit profileName;
|
||||||
}).out;
|
}).out;
|
||||||
|
tracing =
|
||||||
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".tracing."0.1.41" {
|
||||||
|
inherit profileName;
|
||||||
|
}).out;
|
||||||
tracing_subscriber =
|
tracing_subscriber =
|
||||||
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".tracing-subscriber."0.3.19" {
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".tracing-subscriber."0.3.19" {
|
||||||
inherit profileName;
|
inherit profileName;
|
||||||
|
@ -3164,7 +3180,7 @@ else
|
||||||
inherit profileName;
|
inherit profileName;
|
||||||
}).out;
|
}).out;
|
||||||
b64_ct =
|
b64_ct =
|
||||||
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".b64-ct."0.1.2" {
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".b64-ct."0.1.3" {
|
||||||
inherit profileName;
|
inherit profileName;
|
||||||
}).out;
|
}).out;
|
||||||
bincode =
|
bincode =
|
||||||
|
@ -3184,6 +3200,10 @@ else
|
||||||
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".eyre."0.6.12" {
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".eyre."0.6.12" {
|
||||||
inherit profileName;
|
inherit profileName;
|
||||||
}).out;
|
}).out;
|
||||||
|
futures =
|
||||||
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.31" {
|
||||||
|
inherit profileName;
|
||||||
|
}).out;
|
||||||
mime =
|
mime =
|
||||||
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".mime."0.3.17" {
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".mime."0.3.17" {
|
||||||
inherit profileName;
|
inherit profileName;
|
||||||
|
@ -3196,10 +3216,18 @@ else
|
||||||
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".sentry-tower."0.34.0" {
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".sentry-tower."0.34.0" {
|
||||||
inherit profileName;
|
inherit profileName;
|
||||||
}).out;
|
}).out;
|
||||||
|
serde =
|
||||||
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".serde."1.0.215" {
|
||||||
|
inherit profileName;
|
||||||
|
}).out;
|
||||||
tokio =
|
tokio =
|
||||||
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".tokio."1.42.0" {
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".tokio."1.42.0" {
|
||||||
inherit profileName;
|
inherit profileName;
|
||||||
}).out;
|
}).out;
|
||||||
|
tokio_util =
|
||||||
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".tokio-util."0.7.13" {
|
||||||
|
inherit profileName;
|
||||||
|
}).out;
|
||||||
tower_http =
|
tower_http =
|
||||||
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".tower-http."0.6.2" {
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".tower-http."0.6.2" {
|
||||||
inherit profileName;
|
inherit profileName;
|
||||||
|
@ -3226,7 +3254,6 @@ else
|
||||||
[ "axum-core" ]
|
[ "axum-core" ]
|
||||||
[ "bytes" ]
|
[ "bytes" ]
|
||||||
[ "mime" ]
|
[ "mime" ]
|
||||||
[ "tracing" ]
|
|
||||||
];
|
];
|
||||||
dependencies = {
|
dependencies = {
|
||||||
async_trait =
|
async_trait =
|
||||||
|
@ -3238,7 +3265,7 @@ else
|
||||||
inherit profileName;
|
inherit profileName;
|
||||||
}).out;
|
}).out;
|
||||||
b64_ct =
|
b64_ct =
|
||||||
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".b64-ct."0.1.2" {
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".b64-ct."0.1.3" {
|
||||||
inherit profileName;
|
inherit profileName;
|
||||||
}).out;
|
}).out;
|
||||||
bincode =
|
bincode =
|
||||||
|
@ -5111,6 +5138,7 @@ else
|
||||||
[ "async-await" ]
|
[ "async-await" ]
|
||||||
[ "async-await-macro" ]
|
[ "async-await-macro" ]
|
||||||
[ "channel" ]
|
[ "channel" ]
|
||||||
|
[ "default" ]
|
||||||
[ "futures-channel" ]
|
[ "futures-channel" ]
|
||||||
[ "futures-io" ]
|
[ "futures-io" ]
|
||||||
[ "futures-macro" ]
|
[ "futures-macro" ]
|
||||||
|
@ -5351,7 +5379,7 @@ else
|
||||||
inherit profileName;
|
inherit profileName;
|
||||||
}).out;
|
}).out;
|
||||||
tokio_util =
|
tokio_util =
|
||||||
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".tokio-util."0.7.12" {
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".tokio-util."0.7.13" {
|
||||||
inherit profileName;
|
inherit profileName;
|
||||||
}).out;
|
}).out;
|
||||||
tracing =
|
tracing =
|
||||||
|
@ -5409,7 +5437,7 @@ else
|
||||||
inherit profileName;
|
inherit profileName;
|
||||||
}).out;
|
}).out;
|
||||||
tokio_util =
|
tokio_util =
|
||||||
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".tokio-util."0.7.12" {
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".tokio-util."0.7.13" {
|
||||||
inherit profileName;
|
inherit profileName;
|
||||||
}).out;
|
}).out;
|
||||||
tracing =
|
tracing =
|
||||||
|
@ -7269,6 +7297,38 @@ else
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
"registry+https://github.com/rust-lang/crates.io-index".mime_guess."2.0.5" =
|
||||||
|
overridableMkRustCrate
|
||||||
|
(profileName: rec {
|
||||||
|
name = "mime_guess";
|
||||||
|
version = "2.0.5";
|
||||||
|
registry = "registry+https://github.com/rust-lang/crates.io-index";
|
||||||
|
src = fetchCratesIo {
|
||||||
|
inherit name version;
|
||||||
|
sha256 = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e";
|
||||||
|
};
|
||||||
|
features = builtins.concatLists [
|
||||||
|
[ "default" ]
|
||||||
|
[ "rev-mappings" ]
|
||||||
|
];
|
||||||
|
dependencies = {
|
||||||
|
mime =
|
||||||
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".mime."0.3.17" {
|
||||||
|
inherit profileName;
|
||||||
|
}).out;
|
||||||
|
unicase =
|
||||||
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".unicase."2.8.0" {
|
||||||
|
inherit profileName;
|
||||||
|
}).out;
|
||||||
|
};
|
||||||
|
buildDependencies = {
|
||||||
|
unicase =
|
||||||
|
(buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".unicase."2.8.0" {
|
||||||
|
profileName = "__noProfile";
|
||||||
|
}).out;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index".minimal-lexical."0.2.1" =
|
"registry+https://github.com/rust-lang/crates.io-index".minimal-lexical."0.2.1" =
|
||||||
overridableMkRustCrate
|
overridableMkRustCrate
|
||||||
(profileName: rec {
|
(profileName: rec {
|
||||||
|
@ -8815,6 +8875,7 @@ else
|
||||||
[ "rustls-tls" ]
|
[ "rustls-tls" ]
|
||||||
[ "rustls-tls-webpki-roots" ]
|
[ "rustls-tls-webpki-roots" ]
|
||||||
[ "rustls-tls-webpki-roots-no-provider" ]
|
[ "rustls-tls-webpki-roots-no-provider" ]
|
||||||
|
[ "stream" ]
|
||||||
];
|
];
|
||||||
dependencies = {
|
dependencies = {
|
||||||
base64 =
|
base64 =
|
||||||
|
@ -8929,6 +8990,10 @@ else
|
||||||
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".tokio-rustls."0.26.0" {
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".tokio-rustls."0.26.0" {
|
||||||
inherit profileName;
|
inherit profileName;
|
||||||
}).out;
|
}).out;
|
||||||
|
${if !(hostPlatform.parsed.cpu.name == "wasm32") then "tokio_util" else null} =
|
||||||
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".tokio-util."0.7.13" {
|
||||||
|
inherit profileName;
|
||||||
|
}).out;
|
||||||
tower_service =
|
tower_service =
|
||||||
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".tower-service."0.3.3" {
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".tower-service."0.3.3" {
|
||||||
inherit profileName;
|
inherit profileName;
|
||||||
|
@ -8945,6 +9010,10 @@ else
|
||||||
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".wasm-bindgen-futures."0.4.47"
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".wasm-bindgen-futures."0.4.47"
|
||||||
{ inherit profileName; }
|
{ inherit profileName; }
|
||||||
).out;
|
).out;
|
||||||
|
${if hostPlatform.parsed.cpu.name == "wasm32" then "wasm_streams" else null} =
|
||||||
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".wasm-streams."0.4.2" {
|
||||||
|
inherit profileName;
|
||||||
|
}).out;
|
||||||
${if hostPlatform.parsed.cpu.name == "wasm32" then "web_sys" else null} =
|
${if hostPlatform.parsed.cpu.name == "wasm32" then "web_sys" else null} =
|
||||||
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".web-sys."0.3.74" {
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".web-sys."0.3.74" {
|
||||||
inherit profileName;
|
inherit profileName;
|
||||||
|
@ -12344,19 +12413,21 @@ else
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index".tokio-util."0.7.12" =
|
"registry+https://github.com/rust-lang/crates.io-index".tokio-util."0.7.13" =
|
||||||
overridableMkRustCrate
|
overridableMkRustCrate
|
||||||
(profileName: rec {
|
(profileName: rec {
|
||||||
name = "tokio-util";
|
name = "tokio-util";
|
||||||
version = "0.7.12";
|
version = "0.7.13";
|
||||||
registry = "registry+https://github.com/rust-lang/crates.io-index";
|
registry = "registry+https://github.com/rust-lang/crates.io-index";
|
||||||
src = fetchCratesIo {
|
src = fetchCratesIo {
|
||||||
inherit name version;
|
inherit name version;
|
||||||
sha256 = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a";
|
sha256 = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078";
|
||||||
};
|
};
|
||||||
features = builtins.concatLists [
|
features = builtins.concatLists [
|
||||||
[ "codec" ]
|
[ "codec" ]
|
||||||
|
[ "compat" ]
|
||||||
[ "default" ]
|
[ "default" ]
|
||||||
|
[ "futures-io" ]
|
||||||
[ "io" ]
|
[ "io" ]
|
||||||
];
|
];
|
||||||
dependencies = {
|
dependencies = {
|
||||||
|
@ -12368,6 +12439,10 @@ else
|
||||||
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-core."0.3.31" {
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-core."0.3.31" {
|
||||||
inherit profileName;
|
inherit profileName;
|
||||||
}).out;
|
}).out;
|
||||||
|
futures_io =
|
||||||
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-io."0.3.31" {
|
||||||
|
inherit profileName;
|
||||||
|
}).out;
|
||||||
futures_sink =
|
futures_sink =
|
||||||
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-sink."0.3.31" {
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-sink."0.3.31" {
|
||||||
inherit profileName;
|
inherit profileName;
|
||||||
|
@ -13030,6 +13105,18 @@ else
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
"registry+https://github.com/rust-lang/crates.io-index".unicase."2.8.0" =
|
||||||
|
overridableMkRustCrate
|
||||||
|
(profileName: rec {
|
||||||
|
name = "unicase";
|
||||||
|
version = "2.8.0";
|
||||||
|
registry = "registry+https://github.com/rust-lang/crates.io-index";
|
||||||
|
src = fetchCratesIo {
|
||||||
|
inherit name version;
|
||||||
|
sha256 = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df";
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index".unicode-bidi."0.3.17" =
|
"registry+https://github.com/rust-lang/crates.io-index".unicode-bidi."0.3.17" =
|
||||||
overridableMkRustCrate
|
overridableMkRustCrate
|
||||||
(profileName: rec {
|
(profileName: rec {
|
||||||
|
@ -13590,6 +13677,40 @@ else
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
"registry+https://github.com/rust-lang/crates.io-index".wasm-streams."0.4.2" =
|
||||||
|
overridableMkRustCrate
|
||||||
|
(profileName: rec {
|
||||||
|
name = "wasm-streams";
|
||||||
|
version = "0.4.2";
|
||||||
|
registry = "registry+https://github.com/rust-lang/crates.io-index";
|
||||||
|
src = fetchCratesIo {
|
||||||
|
inherit name version;
|
||||||
|
sha256 = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65";
|
||||||
|
};
|
||||||
|
dependencies = {
|
||||||
|
futures_util =
|
||||||
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-util."0.3.31" {
|
||||||
|
inherit profileName;
|
||||||
|
}).out;
|
||||||
|
js_sys =
|
||||||
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".js-sys."0.3.74" {
|
||||||
|
inherit profileName;
|
||||||
|
}).out;
|
||||||
|
wasm_bindgen =
|
||||||
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".wasm-bindgen."0.2.97" {
|
||||||
|
inherit profileName;
|
||||||
|
}).out;
|
||||||
|
wasm_bindgen_futures =
|
||||||
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".wasm-bindgen-futures."0.4.47"
|
||||||
|
{ inherit profileName; }
|
||||||
|
).out;
|
||||||
|
web_sys =
|
||||||
|
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".web-sys."0.3.74" {
|
||||||
|
inherit profileName;
|
||||||
|
}).out;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index".web-sys."0.3.74" =
|
"registry+https://github.com/rust-lang/crates.io-index".web-sys."0.3.74" =
|
||||||
overridableMkRustCrate
|
overridableMkRustCrate
|
||||||
(profileName: rec {
|
(profileName: rec {
|
||||||
|
@ -13612,16 +13733,36 @@ else
|
||||||
[ "Headers" ]
|
[ "Headers" ]
|
||||||
[ "MessageEvent" ]
|
[ "MessageEvent" ]
|
||||||
[ "Performance" ]
|
[ "Performance" ]
|
||||||
|
[ "QueuingStrategy" ]
|
||||||
|
[ "ReadableByteStreamController" ]
|
||||||
[ "ReadableStream" ]
|
[ "ReadableStream" ]
|
||||||
|
[ "ReadableStreamByobReader" ]
|
||||||
|
[ "ReadableStreamByobRequest" ]
|
||||||
|
[ "ReadableStreamDefaultController" ]
|
||||||
|
[ "ReadableStreamDefaultReader" ]
|
||||||
|
[ "ReadableStreamGetReaderOptions" ]
|
||||||
|
[ "ReadableStreamReadResult" ]
|
||||||
|
[ "ReadableStreamReaderMode" ]
|
||||||
|
[ "ReadableStreamType" ]
|
||||||
|
[ "ReadableWritablePair" ]
|
||||||
[ "Request" ]
|
[ "Request" ]
|
||||||
[ "RequestCredentials" ]
|
[ "RequestCredentials" ]
|
||||||
[ "RequestInit" ]
|
[ "RequestInit" ]
|
||||||
[ "RequestMode" ]
|
[ "RequestMode" ]
|
||||||
[ "Response" ]
|
[ "Response" ]
|
||||||
[ "ServiceWorkerGlobalScope" ]
|
[ "ServiceWorkerGlobalScope" ]
|
||||||
|
[ "StreamPipeOptions" ]
|
||||||
|
[ "TransformStream" ]
|
||||||
|
[ "TransformStreamDefaultController" ]
|
||||||
|
[ "Transformer" ]
|
||||||
|
[ "UnderlyingSink" ]
|
||||||
|
[ "UnderlyingSource" ]
|
||||||
[ "Window" ]
|
[ "Window" ]
|
||||||
[ "Worker" ]
|
[ "Worker" ]
|
||||||
[ "WorkerGlobalScope" ]
|
[ "WorkerGlobalScope" ]
|
||||||
|
[ "WritableStream" ]
|
||||||
|
[ "WritableStreamDefaultController" ]
|
||||||
|
[ "WritableStreamDefaultWriter" ]
|
||||||
[ "default" ]
|
[ "default" ]
|
||||||
[ "std" ]
|
[ "std" ]
|
||||||
];
|
];
|
||||||
|
|
|
@ -95,9 +95,9 @@ impl CaStore {
|
||||||
{
|
{
|
||||||
let mut reader = Box::pin(reader);
|
let mut reader = Box::pin(reader);
|
||||||
let string_id = lexicographic_base64::encode(id.to_be_bytes());
|
let string_id = lexicographic_base64::encode(id.to_be_bytes());
|
||||||
|
|
||||||
info!("Starting multipart upload {id}");
|
|
||||||
let source_fname = format!("temp/{string_id}");
|
let source_fname = format!("temp/{string_id}");
|
||||||
|
|
||||||
|
/*info!("Starting multipart upload {id}");
|
||||||
let multipart_result = self
|
let multipart_result = self
|
||||||
.client
|
.client
|
||||||
.create_multipart_upload()
|
.create_multipart_upload()
|
||||||
|
@ -163,7 +163,28 @@ impl CaStore {
|
||||||
.set_upload_id(multipart_result.upload_id)
|
.set_upload_id(multipart_result.upload_id)
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
.context("Completing multipart upload")?;
|
.context("Completing multipart upload")?;*/
|
||||||
|
|
||||||
|
let hasher = Arc::new(Mutex::new(Hasher::new()));
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
reader.read_to_end(&mut buf).await?;
|
||||||
|
let buf = Bytes::from(buf);
|
||||||
|
let buf2 = buf.clone();
|
||||||
|
let hasher2 = Arc::clone(&hasher);
|
||||||
|
spawn_blocking(move || {
|
||||||
|
hasher2.blocking_lock().update_rayon(&buf2);
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
self.client
|
||||||
|
.put_object()
|
||||||
|
.bucket(&*self.bucket)
|
||||||
|
.key(&source_fname)
|
||||||
|
.body(ByteStream::from(buf.to_vec()))
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.context("Uploading file")?;
|
||||||
|
|
||||||
|
let hash = hasher.lock().await.finalize();
|
||||||
|
|
||||||
let target_fname = lexicographic_base64::encode(hash.as_bytes());
|
let target_fname = lexicographic_base64::encode(hash.as_bytes());
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,11 @@ bincode = "2.0.0-rc.3"
|
||||||
chir-rs-http-api = { version = "0.1.0", path = "../chir-rs-http-api" }
|
chir-rs-http-api = { version = "0.1.0", path = "../chir-rs-http-api" }
|
||||||
clap = { version = "4.5.21", features = ["derive"] }
|
clap = { version = "4.5.21", features = ["derive"] }
|
||||||
color-eyre = "0.6.3"
|
color-eyre = "0.6.3"
|
||||||
|
dotenvy = "0.15.7"
|
||||||
eyre = "0.6.12"
|
eyre = "0.6.12"
|
||||||
reqwest = { version = "0.12.9", default-features = false, features = [
|
mime = "0.3.17"
|
||||||
"rustls-tls",
|
mime_guess = "2.0.5"
|
||||||
] }
|
reqwest = { version = "0.12.9", default-features = false, features = ["rustls-tls", "stream"] }
|
||||||
tokio = { version = "1.41.1", features = ["macros", "rt-multi-thread"] }
|
tokio = { version = "1.41.1", features = ["fs", "macros", "rt-multi-thread"] }
|
||||||
|
tracing = "0.1.41"
|
||||||
tracing-subscriber = "0.3.19"
|
tracing-subscriber = "0.3.19"
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
use std::collections::HashSet;
|
use std::{collections::HashSet, future::Future, path::Path, pin::Pin};
|
||||||
|
|
||||||
use chir_rs_http_api::{
|
use chir_rs_http_api::{
|
||||||
auth::{LoginRequest, PasetoToken, Scope},
|
auth::{LoginRequest, PasetoToken, Scope},
|
||||||
errors::APIError,
|
errors::APIError,
|
||||||
};
|
};
|
||||||
use clap::{arg, Parser, Subcommand};
|
use clap::{arg, Parser, Subcommand};
|
||||||
use eyre::Result;
|
use eyre::{eyre, Context as _, OptionExt as _, Result};
|
||||||
|
use mime_guess::{Mime, MimeGuess};
|
||||||
|
use reqwest::Body;
|
||||||
|
use tokio::join;
|
||||||
|
use tracing::instrument;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(version, about, long_about = None)]
|
#[command(version, about, long_about = None)]
|
||||||
|
@ -24,6 +28,18 @@ enum Command {
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
password: String,
|
password: String,
|
||||||
},
|
},
|
||||||
|
Upload {
|
||||||
|
#[arg(short, long)]
|
||||||
|
source: String,
|
||||||
|
#[arg(short, long)]
|
||||||
|
dest: String,
|
||||||
|
},
|
||||||
|
UploadDir {
|
||||||
|
#[arg(short, long)]
|
||||||
|
source: String,
|
||||||
|
#[arg(short, long)]
|
||||||
|
dest: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn login(url: String, username: String, password: String) -> Result<()> {
|
async fn login(url: String, username: String, password: String) -> Result<()> {
|
||||||
|
@ -58,14 +74,76 @@ async fn login(url: String, username: String, password: String) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(source))]
|
||||||
|
async fn upload(url: String, source: impl AsRef<Path>, dest: String) -> Result<()> {
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
let token = std::env::var("CHIR_RS_TOKEN")?;
|
||||||
|
let file = tokio::fs::File::open(&source).await?;
|
||||||
|
let res = client
|
||||||
|
.post(format!("{url}{dest}"))
|
||||||
|
.header(
|
||||||
|
"Content-Type",
|
||||||
|
MimeGuess::from_path(&source)
|
||||||
|
.first()
|
||||||
|
.unwrap_or(mime::APPLICATION_OCTET_STREAM)
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
|
.header("Authorization", format!("Bearer {token}"))
|
||||||
|
.body(Body::from(file))
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
if !res.status().is_success() {
|
||||||
|
let response = res.bytes().await?;
|
||||||
|
let response: APIError =
|
||||||
|
bincode::decode_from_slice(&response, bincode::config::standard())?.0;
|
||||||
|
Err(response).with_context(|| format!("Uploading to {dest}"))?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(source))]
|
||||||
|
async fn upload_dir(url: String, source: impl AsRef<Path>, dest: String) -> Result<()> {
|
||||||
|
let mut dir = tokio::fs::read_dir(source).await?;
|
||||||
|
while let Some(ent) = dir.next_entry().await? {
|
||||||
|
let file_type = ent.file_type().await?;
|
||||||
|
let file_name_str = ent
|
||||||
|
.file_name()
|
||||||
|
.into_string()
|
||||||
|
.map_err(|_| eyre!("Invalid file name encountered"))?;
|
||||||
|
let tgt = if dest.is_empty() {
|
||||||
|
file_name_str.clone()
|
||||||
|
} else {
|
||||||
|
format!("{dest}/{file_name_str}")
|
||||||
|
};
|
||||||
|
if file_type.is_dir() {
|
||||||
|
let sub_fut: Pin<Box<dyn Future<Output = Result<()>>>> =
|
||||||
|
Box::pin(upload_dir(url.clone(), ent.path(), tgt));
|
||||||
|
sub_fut.await?;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if file_name_str == "index.html" {
|
||||||
|
if dest.is_empty() {
|
||||||
|
upload(url.clone(), ent.path(), "".to_string()).await?;
|
||||||
|
} else {
|
||||||
|
upload(url.clone(), ent.path(), format!("{dest}/")).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
upload(url.clone(), ent.path(), tgt).await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
color_eyre::install().ok();
|
color_eyre::install().ok();
|
||||||
|
dotenvy::dotenv().ok();
|
||||||
tracing_subscriber::fmt::init();
|
tracing_subscriber::fmt::init();
|
||||||
let matches = Args::parse();
|
let matches = Args::parse();
|
||||||
|
|
||||||
match matches.command {
|
match matches.command {
|
||||||
Command::Login { username, password } => login(matches.url, username, password).await?,
|
Command::Login { username, password } => login(matches.url, username, password).await?,
|
||||||
|
Command::Upload { source, dest } => upload(matches.url, source, dest).await?,
|
||||||
|
Command::UploadDir { source, dest } => upload_dir(matches.url, source, dest).await?,
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -165,11 +165,11 @@ impl File {
|
||||||
/// Creates a new file
|
/// Creates a new file
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// This function returns an error if a database error occurs when writing, or there is a conflict
|
/// This function returns an error if a database error occurs when writing
|
||||||
#[instrument(skip(db))]
|
#[instrument(skip(db))]
|
||||||
pub async fn new(db: &Database, path: &str, mime: &str, hash: &Hash) -> Result<()> {
|
pub async fn new(db: &Database, path: &str, mime: &str, hash: &Hash) -> Result<()> {
|
||||||
query_as(
|
query_as(
|
||||||
r#"INSERT INTO file_map ("file_path", "mime", "b3hash") VALUES ($1, $2, $3) RETURNING *"#,
|
r#"INSERT INTO file_map ("file_path", "mime", "b3hash") VALUES ($1, $2, $3) ON CONFLICT ("file_path", "mime") DO UPDATE SET "b3hash" = $3 RETURNING *"#,
|
||||||
)
|
)
|
||||||
.bind(path)
|
.bind(path)
|
||||||
.bind(mime)
|
.bind(mime)
|
||||||
|
|
|
@ -23,7 +23,7 @@ pub async fn expire(db: &Database) -> Result<()> {
|
||||||
let oldest_acceptable_id = oldest_acceptable_id.to_be_bytes();
|
let oldest_acceptable_id = oldest_acceptable_id.to_be_bytes();
|
||||||
#[expect(clippy::panic, reason = "sqlx moment")]
|
#[expect(clippy::panic, reason = "sqlx moment")]
|
||||||
query!(
|
query!(
|
||||||
r#"DELETE FROM "session_scopes" WHERE session_id < $1"#,
|
r#"DELETE FROM "sessions" WHERE id < $1"#,
|
||||||
&oldest_acceptable_id
|
&oldest_acceptable_id
|
||||||
)
|
)
|
||||||
.execute(&*db.0)
|
.execute(&*db.0)
|
||||||
|
|
|
@ -4,7 +4,7 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
axum = ["axum-core", "async-trait", "bytes", "mime", "tracing"]
|
axum = ["axum-core", "async-trait", "bytes", "mime"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-trait = { version = "0.1.83", optional = true }
|
async-trait = { version = "0.1.83", optional = true }
|
||||||
|
@ -18,7 +18,7 @@ http = "1.1.0"
|
||||||
mime = { version = "0.3.17", optional = true }
|
mime = { version = "0.3.17", optional = true }
|
||||||
serde = { version = "1.0.215", features = ["derive"] }
|
serde = { version = "1.0.215", features = ["derive"] }
|
||||||
thiserror = "2.0.3"
|
thiserror = "2.0.3"
|
||||||
tracing = { version = "0.1.40", optional = true }
|
tracing = { version = "0.1.40" }
|
||||||
|
|
||||||
[lints.rust]
|
[lints.rust]
|
||||||
deprecated-safe = "forbid"
|
deprecated-safe = "forbid"
|
||||||
|
|
|
@ -4,13 +4,27 @@ use bincode::{Decode, Encode};
|
||||||
use educe::Educe;
|
use educe::Educe;
|
||||||
use eyre::{bail, eyre, Result};
|
use eyre::{bail, eyre, Result};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{collections::HashSet, fmt::Debug};
|
use std::{
|
||||||
|
collections::HashSet,
|
||||||
|
fmt::{Debug, Display},
|
||||||
|
};
|
||||||
|
|
||||||
/// List of supported scopes for authentication
|
/// List of supported scopes for authentication
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Encode, Decode, Hash)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Encode, Decode, Hash)]
|
||||||
pub enum Scope {
|
pub enum Scope {
|
||||||
/// Full scope granted by logging in.
|
/// Full scope granted by logging in.
|
||||||
Full,
|
Full,
|
||||||
|
/// The ability to create or update files.
|
||||||
|
CreateUpdateFile,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Scope {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match *self {
|
||||||
|
Self::Full => write!(f, "Full permissions"),
|
||||||
|
Self::CreateUpdateFile => write!(f, "Create and update files"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scope {
|
impl Scope {
|
||||||
|
@ -19,6 +33,7 @@ impl Scope {
|
||||||
pub const fn to_i64(self) -> i64 {
|
pub const fn to_i64(self) -> i64 {
|
||||||
match self {
|
match self {
|
||||||
Self::Full => 0,
|
Self::Full => 0,
|
||||||
|
Self::CreateUpdateFile => 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +44,7 @@ impl Scope {
|
||||||
pub fn from_i64(id: i64) -> Result<Self> {
|
pub fn from_i64(id: i64) -> Result<Self> {
|
||||||
match id {
|
match id {
|
||||||
0 => Ok(Self::Full),
|
0 => Ok(Self::Full),
|
||||||
|
1 => Ok(Self::CreateUpdateFile),
|
||||||
_ => bail!("Invalid scope ID {id}"),
|
_ => bail!("Invalid scope ID {id}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,9 @@
|
||||||
use bincode::{Decode, Encode};
|
use bincode::{Decode, Encode};
|
||||||
use http::StatusCode;
|
use http::StatusCode;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use tracing::error;
|
||||||
|
|
||||||
|
use crate::auth::Scope;
|
||||||
|
|
||||||
/// The main error type
|
/// The main error type
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, Error)]
|
#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, Error)]
|
||||||
|
@ -31,7 +34,7 @@ pub enum APIError {
|
||||||
#[error("Invalid payload")]
|
#[error("Invalid payload")]
|
||||||
PayloadInvalid,
|
PayloadInvalid,
|
||||||
/// Returned when the error is unknown
|
/// Returned when the error is unknown
|
||||||
#[error("Unknown Error")]
|
#[error("Unknown Error {0}")]
|
||||||
Unknown(String),
|
Unknown(String),
|
||||||
/// Returned when there is a database error
|
/// Returned when there is a database error
|
||||||
#[error("Database error: {0}")]
|
#[error("Database error: {0}")]
|
||||||
|
@ -57,6 +60,16 @@ pub enum APIError {
|
||||||
/// Invalid session
|
/// Invalid session
|
||||||
#[error("Invalid session")]
|
#[error("Invalid session")]
|
||||||
InvalidSession,
|
InvalidSession,
|
||||||
|
/// Missing scope
|
||||||
|
#[error("Missing required scope for request: {0}")]
|
||||||
|
MissingScope(Scope),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<eyre::Report> for APIError {
|
||||||
|
fn from(value: eyre::Report) -> Self {
|
||||||
|
error!("Error while handling request: {value:?}");
|
||||||
|
Self::Unknown(format!("Error while handling request: {value:?}"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl APIError {
|
impl APIError {
|
||||||
|
@ -75,7 +88,8 @@ impl APIError {
|
||||||
| Self::InvalidAuthorizationHeader(_)
|
| Self::InvalidAuthorizationHeader(_)
|
||||||
| Self::InvalidAuthorizationMethod(_, _)
|
| Self::InvalidAuthorizationMethod(_, _)
|
||||||
| Self::Unauthorized
|
| Self::Unauthorized
|
||||||
| Self::InvalidSession => StatusCode::UNAUTHORIZED,
|
| Self::InvalidSession
|
||||||
|
| Self::MissingScope(_) => StatusCode::UNAUTHORIZED,
|
||||||
Self::Unknown(_) | Self::DatabaseError(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
Self::Unknown(_) | Self::DatabaseError(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,10 +20,13 @@ chir-rs-misc = { version = "0.1.0", path = "../chir-rs-misc", features = [
|
||||||
] }
|
] }
|
||||||
chrono = "0.4.38"
|
chrono = "0.4.38"
|
||||||
eyre = "0.6.12"
|
eyre = "0.6.12"
|
||||||
|
futures = "0.3.31"
|
||||||
mime = "0.3.17"
|
mime = "0.3.17"
|
||||||
rusty_paseto = { version = "0.7.1", default-features = false, features = ["batteries_included", "v4_local"] }
|
rusty_paseto = { version = "0.7.1", default-features = false, features = ["batteries_included", "v4_local"] }
|
||||||
sentry-tower = { version = "0.34.0", features = ["axum", "axum-matched-path"] }
|
sentry-tower = { version = "0.34.0", features = ["axum", "axum-matched-path"] }
|
||||||
|
serde = { version = "1.0.215", features = ["derive"] }
|
||||||
tokio = { version = "1.41.1", features = ["fs", "net"] }
|
tokio = { version = "1.41.1", features = ["fs", "net"] }
|
||||||
|
tokio-util = { version = "0.7.13", features = ["compat"] }
|
||||||
tower-http = { version = "0.6.2", features = ["trace"] }
|
tower-http = { version = "0.6.2", features = ["trace"] }
|
||||||
tracing = "0.1.40"
|
tracing = "0.1.40"
|
||||||
unicode-normalization = "0.1.24"
|
unicode-normalization = "0.1.24"
|
||||||
|
|
|
@ -20,6 +20,23 @@ use crate::AppState;
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct AuthHeader(pub String, pub HashSet<Scope>);
|
pub struct AuthHeader(pub String, pub HashSet<Scope>);
|
||||||
|
|
||||||
|
impl AuthHeader {
|
||||||
|
/// Checks whether or not a scope is granted for the session
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// This function returns an error if the current session does not have a scope granted.
|
||||||
|
pub fn assert_scope(&self, scope: Scope) -> Result<(), APIError> {
|
||||||
|
if self.1.contains(&Scope::Full) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
if self.1.contains(&scope) {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(APIError::MissingScope(scope))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl FromRequestParts<AppState> for AuthHeader {
|
impl FromRequestParts<AppState> for AuthHeader {
|
||||||
type Rejection = APIError;
|
type Rejection = APIError;
|
||||||
|
|
|
@ -2,20 +2,24 @@
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
body::Body,
|
body::Body,
|
||||||
extract::State,
|
extract::{Query, State},
|
||||||
http::{
|
http::{
|
||||||
header::{ACCEPT, CACHE_CONTROL, CONTENT_LENGTH, CONTENT_TYPE, ETAG, IF_NONE_MATCH},
|
header::{ACCEPT, CACHE_CONTROL, CONTENT_LENGTH, CONTENT_TYPE, ETAG, IF_NONE_MATCH},
|
||||||
HeaderMap, StatusCode, Uri,
|
HeaderMap, Request, StatusCode, Uri,
|
||||||
},
|
},
|
||||||
response::Response,
|
response::Response,
|
||||||
};
|
};
|
||||||
use chir_rs_db::file::File;
|
use chir_rs_db::file::File;
|
||||||
|
use chir_rs_http_api::{auth::Scope, errors::APIError};
|
||||||
use chir_rs_misc::lexicographic_base64;
|
use chir_rs_misc::lexicographic_base64;
|
||||||
use eyre::Context as _;
|
use eyre::Context as _;
|
||||||
|
use futures::TryStreamExt;
|
||||||
use mime::MimeIter;
|
use mime::MimeIter;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use tokio_util::compat::FuturesAsyncReadCompatExt as _;
|
||||||
use tracing::{debug, error, info};
|
use tracing::{debug, error, info};
|
||||||
|
|
||||||
use crate::AppState;
|
use crate::{auth::req_auth::auth_header::AuthHeader, AppState};
|
||||||
|
|
||||||
/// Formats an eyre error message
|
/// Formats an eyre error message
|
||||||
#[expect(clippy::expect_used, reason = "fatal error in error handling function")]
|
#[expect(clippy::expect_used, reason = "fatal error in error handling function")]
|
||||||
|
@ -153,3 +157,35 @@ pub async fn serve_files(
|
||||||
.with_context(|| format!("Serving file for {path}"))
|
.with_context(|| format!("Serving file for {path}"))
|
||||||
.map_err(format_error)
|
.map_err(format_error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a static file
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This function returns an error if the request fails.
|
||||||
|
pub async fn create_files(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
session: AuthHeader,
|
||||||
|
uri: Uri,
|
||||||
|
headers: HeaderMap,
|
||||||
|
req: Request<Body>,
|
||||||
|
) -> Result<[u8; 32], APIError> {
|
||||||
|
session.assert_scope(Scope::CreateUpdateFile)?;
|
||||||
|
let mime = headers
|
||||||
|
.get(CONTENT_TYPE)
|
||||||
|
.ok_or_else(|| APIError::ClientMissingContentType {
|
||||||
|
expected: "*/*".to_string(),
|
||||||
|
})?
|
||||||
|
.to_str()
|
||||||
|
.map_err(|e| APIError::ClientInvalidContentType {
|
||||||
|
expected: "*/*".to_string(),
|
||||||
|
received: format!("{e:?}"),
|
||||||
|
})?;
|
||||||
|
let mut stream =
|
||||||
|
TryStreamExt::map_err(req.into_body().into_data_stream(), std::io::Error::other)
|
||||||
|
.into_async_read()
|
||||||
|
.compat();
|
||||||
|
let result = state.ca.upload(&mut stream).await?;
|
||||||
|
File::new(&state.db, uri.path(), mime, &result).await?;
|
||||||
|
Ok(*result.as_bytes())
|
||||||
|
}
|
||||||
|
|
|
@ -80,7 +80,7 @@ pub async fn main(cfg: Arc<ChirRs>, db: Database, castore: CaStore) -> Result<()
|
||||||
get(|| async move { metric_handle.render() }),
|
get(|| async move { metric_handle.render() }),
|
||||||
)
|
)
|
||||||
.route("/.api/auth/login", post(auth::password_login::login))
|
.route("/.api/auth/login", post(auth::password_login::login))
|
||||||
.fallback(get(ca_server::serve_files))
|
.fallback(get(ca_server::serve_files).post(ca_server::create_files))
|
||||||
.with_state(AppState {
|
.with_state(AppState {
|
||||||
db,
|
db,
|
||||||
ca: castore,
|
ca: castore,
|
||||||
|
|
Loading…
Reference in a new issue