From c8da87e0dd792cddbd42a04fe6854e0cf2254e3e Mon Sep 17 00:00:00 2001 From: kieran Date: Sun, 22 Sep 2024 14:26:23 +0100 Subject: [PATCH] feat: rename / upgrades --- .drone.yml | 2 +- Cargo.lock | 157 +++++++++++++++++++--------------------- Cargo.toml | 15 ++-- Dockerfile | 2 +- README.md | 9 ++- config.prod.toml | 2 +- config.toml | 4 +- docker-compose.prod.yml | 4 +- docker-compose.yml | 2 +- src/auth/blossom.rs | 24 ++++++ src/auth/nip98.rs | 2 +- src/blob.rs | 27 ------- src/main.rs | 3 +- src/processing/webp.rs | 2 +- src/routes/blossom.rs | 116 ++++++++++++++++++++++++++--- src/routes/mod.rs | 60 +++++++++++++-- src/routes/nip96.rs | 49 ++----------- src/settings.rs | 2 +- ui/index.html | 4 +- 19 files changed, 289 insertions(+), 197 deletions(-) delete mode 100644 src/blob.rs diff --git a/.drone.yml b/.drone.yml index d885a8f..e2331b3 100644 --- a/.drone.yml +++ b/.drone.yml @@ -18,5 +18,5 @@ steps: - dockerd & - docker login -u kieran -p $TOKEN git.v0l.io - docker login -u voidic -p $TOKEN_DOCKER - - docker buildx build --push -t git.v0l.io/kieran/void-cat-rs:latest -t voidic/void-cat-rs:latest . + - docker buildx build --push -t git.v0l.io/kieran/route96:latest -t voidic/route96:latest . - kill $(cat /var/run/docker.pid) \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index d09cfba..08021d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -120,7 +120,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -131,7 +131,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -211,22 +211,22 @@ checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" [[package]] name = "bindgen" -version = "0.64.0" +version = "0.69.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "cexpr", "clang-sys", + "itertools", "lazy_static", "lazycell", - "peeking_take_while", "proc-macro2", "quote", "regex", "rustc-hash 1.1.0", "shlex", - "syn 1.0.109", + "syn", ] [[package]] @@ -358,7 +358,7 @@ checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -705,7 +705,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.76", + "syn", ] [[package]] @@ -716,7 +716,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -748,7 +748,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -781,7 +781,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -804,7 +804,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -859,7 +859,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -932,14 +932,13 @@ checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "ffmpeg-sys-the-third" -version = "1.1.1+ffmpeg-6.0" +version = "2.0.0+ffmpeg-7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94a4b2e9c02074c0ee85661b23b3ac849bad6afc554b503c183975f5e2e0d3de" +checksum = "a82bfdb0a7925996707f0a7dc37b2f3251ff5a15d26e78c586adb60c240dedc5" dependencies = [ "bindgen", "cc", "libc", - "num_cpus", "pkg-config", "vcpkg", ] @@ -1641,6 +1640,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -2001,7 +2009,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -2123,15 +2131,9 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.76", + "syn", ] -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -2178,7 +2180,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -2209,7 +2211,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -2313,7 +2315,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn", "version_check", "yansi", ] @@ -2497,7 +2499,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -2652,7 +2654,7 @@ dependencies = [ "proc-macro2", "quote", "rocket_http", - "syn 2.0.76", + "syn", "unicode-xid", "version_check", ] @@ -2696,6 +2698,35 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "route96" +version = "0.2.0" +dependencies = [ + "anyhow", + "base64 0.22.1", + "blurhash", + "candle-core", + "candle-nn", + "candle-transformers", + "chrono", + "config", + "ffmpeg-sys-the-third", + "hex", + "libc", + "log", + "nostr", + "pretty_env_logger", + "rocket", + "serde", + "serde_with", + "sha2", + "sqlx", + "tokio", + "ureq", + "url", + "uuid", +] + [[package]] name = "rsa" version = "0.9.6" @@ -2907,7 +2938,7 @@ checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -2980,7 +3011,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -3159,7 +3190,7 @@ dependencies = [ "quote", "sqlx-core", "sqlx-macros-core", - "syn 2.0.76", + "syn", ] [[package]] @@ -3182,7 +3213,7 @@ dependencies = [ "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 2.0.76", + "syn", "tempfile", "tokio", "url", @@ -3341,17 +3372,6 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.76" @@ -3380,7 +3400,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -3436,7 +3456,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -3529,7 +3549,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -3660,7 +3680,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -3857,35 +3877,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "void_cat" -version = "0.1.1" -dependencies = [ - "anyhow", - "base64 0.22.1", - "blurhash", - "candle-core", - "candle-nn", - "candle-transformers", - "chrono", - "config", - "ffmpeg-sys-the-third", - "hex", - "libc", - "log", - "nostr", - "pretty_env_logger", - "rocket", - "serde", - "serde_with", - "sha2", - "sqlx", - "tokio", - "ureq", - "url", - "uuid", -] - [[package]] name = "walkdir" version = "2.5.0" @@ -3939,7 +3930,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.76", + "syn", "wasm-bindgen-shared", ] @@ -3973,7 +3964,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4287,7 +4278,7 @@ checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn", "synstructure", ] @@ -4309,7 +4300,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -4329,7 +4320,7 @@ checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn", "synstructure", ] diff --git a/Cargo.toml b/Cargo.toml index 93d4cfa..95a4516 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,8 @@ [package] -name = "void_cat" -version = "0.1.1" +name = "route96" +version = "0.2.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [features] default = ["nip96", "blossom"] labels = ["nip96", "dep:candle-core", "dep:candle-nn", "dep:candle-transformers"] @@ -26,12 +24,13 @@ sha2 = "0.10.8" sqlx = { version = "0.8.1", features = ["mysql", "runtime-tokio", "chrono"] } config = { version = "0.14.0", features = ["toml"] } chrono = { version = "0.4.38", features = ["serde"] } -ffmpeg-sys-the-third = { version = "1.1.1", features = ["default"], optional = true } -libc = { version = "0.2.153", optional = true } -blurhash = { version = "0.2.1", optional = true } -ureq = { version = "2.9.7", features = ["json"] } url = "2.5.0" serde_with = { version = "3.8.1", features = ["hex"] } +ureq = { version = "2.9.7", features = ["json"] } + +libc = { version = "0.2.153", optional = true } +blurhash = { version = "0.2.1", optional = true } +ffmpeg-sys-the-third = { version = "2.0.0+ffmpeg-7.0", features = ["default"], optional = true } candle-core = { git = "https://github.com/huggingface/candle.git", version = "^0.6.1", optional = true } candle-nn = { git = "https://github.com/huggingface/candle.git", version = "^0.6.1", optional = true } candle-transformers = { git = "https://github.com/huggingface/candle.git", version = "^0.6.1", optional = true } diff --git a/Dockerfile b/Dockerfile index 9ceda48..c26d88a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,4 +38,4 @@ RUN apt update && \ COPY --from=build /app/build . COPY --from=build /app/src/ui ui COPY --from=build /app/ffmpeg/lib/ /lib -ENTRYPOINT ["./bin/void_cat"] \ No newline at end of file +ENTRYPOINT ["./bin/route96"] \ No newline at end of file diff --git a/README.md b/README.md index d2300be..50458b3 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,13 @@ -# void-cat-rs +# route96 Image hosting service ## Features - [NIP-96 Support](https://github.com/nostr-protocol/nips/blob/master/96.md) - [Blossom Support](https://github.com/hzrd149/blossom/blob/master/buds/01.md) + - [BUD-01](https://github.com/hzrd149/blossom/blob/master/buds/01.md) + - [BUD-02](https://github.com/hzrd149/blossom/blob/master/buds/02.md) + - [BUD-06](https://github.com/hzrd149/blossom/blob/master/buds/06.md) - Image compression to WebP (FFMPEG, NIP-96 only) - Blurhash calculation (NIP-96 only) - AI image labeling ([ViT224](https://huggingface.co/google/vit-base-patch16-224)) @@ -15,7 +18,7 @@ Image hosting service ## Running ### Docker Compose -The easiest way to run `void-cat-rs` is to use `docker compose` +The easiest way to run `route96` is to use `docker compose` ```bash docker compose -f docker-compose.prod.yml up @@ -27,7 +30,7 @@ docker run --rm -it \ -p 8000:8000 \ -v ./config.toml:/app/config.toml \ -e "RUST_LOG=info" \ - voidic/void-cat-rs + voidic/route96 ``` ## Building diff --git a/config.prod.toml b/config.prod.toml index 0e503bc..b6edc70 100644 --- a/config.prod.toml +++ b/config.prod.toml @@ -2,7 +2,7 @@ listen = "0.0.0.0:8000" # Database connection string (MYSQL) -database = "mysql://root:root@db:3306/void_cat" +database = "mysql://root:root@db:3306/route96" # Directory to store uploads storage_dir = "/app/data" diff --git a/config.toml b/config.toml index c7cbbe6..9ea84e3 100644 --- a/config.toml +++ b/config.toml @@ -2,7 +2,7 @@ listen = "127.0.0.1:8000" # Database connection string (MYSQL) -database = "mysql://root:root@localhost:3366/void_cat" +database = "mysql://root:root@localhost:3366/route96" # Directory to store uploads storage_dir = "./data" @@ -20,4 +20,4 @@ public_url = "http://localhost:8000" # vit_model_path = "model.safetennsors" # Webhook api endpoint -webhook_url = "https://api.snort.social/api/v1/media/webhook" \ No newline at end of file +# webhook_url = "https://api.snort.social/api/v1/media/webhook" \ No newline at end of file diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index c60c3c1..a2866db 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -6,11 +6,11 @@ services: image: mariadb environment: - "MARIADB_ROOT_PASSWORD=root" - - "MARIADB_DATABASE=void_cat" + - "MARIADB_DATABASE=route96" volumes: - "db:/var/lib/mysql" app: - image: voidic/void-cat-rs + image: voidic/route96 #build: . environment: - "RUST_LOG=info" diff --git a/docker-compose.yml b/docker-compose.yml index 7f298b8..e294055 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,7 @@ services: image: mariadb environment: - "MARIADB_ROOT_PASSWORD=root" - - "MARIADB_DATABASE=void_cat" + - "MARIADB_DATABASE=route96" ports: - "3366:3306" volumes: diff --git a/src/auth/blossom.rs b/src/auth/blossom.rs index 8025f14..87f1fd1 100644 --- a/src/auth/blossom.rs +++ b/src/auth/blossom.rs @@ -7,6 +7,9 @@ use rocket::{async_trait, Request}; pub struct BlossomAuth { pub content_type: Option, + pub x_content_type: Option, + pub x_sha_256: Option, + pub x_content_length: Option, pub event: Event, } @@ -65,6 +68,27 @@ impl<'r> FromRequest<'r> for BlossomAuth { None } }), + x_sha_256: request.headers().iter().find_map(|h| { + if h.name == "x-sha-256" { + Some(h.value.to_string()) + } else { + None + } + }), + x_content_length: request.headers().iter().find_map(|h| { + if h.name == "x-content-length" { + Some(h.value.parse().unwrap()) + } else { + None + } + }), + x_content_type: request.headers().iter().find_map(|h| { + if h.name == "x-content-type" { + Some(h.value.to_string()) + } else { + None + } + }), }) } else { Outcome::Error((Status::new(403), "Auth scheme must be Nostr")) diff --git a/src/auth/nip98.rs b/src/auth/nip98.rs index 46062e4..5e05f1a 100644 --- a/src/auth/nip98.rs +++ b/src/auth/nip98.rs @@ -9,7 +9,7 @@ use rocket::request::{FromRequest, Outcome}; pub struct Nip98Auth { pub content_type: Option, - pub content_length: Option, + pub content_length: Option, pub event: Event, } diff --git a/src/blob.rs b/src/blob.rs deleted file mode 100644 index 4573198..0000000 --- a/src/blob.rs +++ /dev/null @@ -1,27 +0,0 @@ -use serde::{Deserialize, Serialize}; - -use crate::db::FileUpload; - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(crate = "rocket::serde")] -pub struct BlobDescriptor { - pub url: String, - pub sha256: String, - pub size: u64, - #[serde(rename = "type", skip_serializing_if = "Option::is_none")] - pub mime_type: Option, - pub created: u64, -} - -impl BlobDescriptor { - pub fn from_upload(value: &FileUpload, public_url: &String) -> Self { - let id_hex = hex::encode(&value.id); - Self { - url: format!("{}/{}", public_url, &id_hex), - sha256: id_hex, - size: value.size, - mime_type: Some(value.mime_type.clone()), - created: value.created.timestamp() as u64, - } - } -} diff --git a/src/main.rs b/src/main.rs index 98947f7..d475cfb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,6 @@ use crate::settings::Settings; use crate::webhook::Webhook; mod auth; -mod blob; mod cors; mod db; mod filesystem; @@ -55,7 +54,7 @@ async fn main() -> Result<(), Error> { .limit("file", upload_limit) .limit("data-form", upload_limit) .limit("form", upload_limit); - config.ident = Ident::try_new("void-cat-rs").unwrap(); + config.ident = Ident::try_new("route96").unwrap(); let mut rocket = rocket::Rocket::custom(config) .manage(FileStore::new(settings.clone())) diff --git a/src/processing/webp.rs b/src/processing/webp.rs index 54ccf98..1f23d87 100644 --- a/src/processing/webp.rs +++ b/src/processing/webp.rs @@ -180,7 +180,7 @@ impl WebpProcessor { AVMEDIA_TYPE_AUDIO => { (*encoder_ctx).sample_rate = (*(*in_stream).codecpar).sample_rate; (*encoder_ctx).sample_fmt = transmute((*(*in_stream).codecpar).format); - (*encoder_ctx).ch_layout = (*(*in_stream).codecpar).ch_layout; + (*encoder_ctx).ch_layout = (*(*in_stream).codecpar).ch_layout.clone(); (*encoder_ctx).time_base = (*in_stream).time_base; (*stream).time_base = (*encoder_ctx).time_base; } diff --git a/src/routes/blossom.rs b/src/routes/blossom.rs index 584ca1f..e6bd286 100644 --- a/src/routes/blossom.rs +++ b/src/routes/blossom.rs @@ -4,27 +4,54 @@ use log::error; use nostr::prelude::hex; use nostr::{Alphabet, SingleLetterTag, TagKind}; use rocket::data::ByteUnit; -use rocket::http::Status; +use rocket::http::{Header, Status}; use rocket::response::Responder; use rocket::serde::json::Json; -use rocket::{routes, Data, Route, State}; +use rocket::{routes, Data, Request, Response, Route, State}; use serde::{Deserialize, Serialize}; use crate::auth::blossom::BlossomAuth; -use crate::blob::BlobDescriptor; -use crate::db::Database; +use crate::db::{Database, FileUpload}; use crate::filesystem::FileStore; -use crate::routes::delete_file; +use crate::routes::{delete_file, Nip94Event}; use crate::settings::Settings; use crate::webhook::Webhook; + +#[derive(Debug, Clone, Serialize)] +#[serde(crate = "rocket::serde")] +pub struct BlobDescriptor { + pub url: String, + pub sha256: String, + pub size: u64, + #[serde(rename = "type", skip_serializing_if = "Option::is_none")] + pub mime_type: Option, + pub created: u64, + #[serde(rename = "nip94", skip_serializing_if = "Option::is_none")] + pub nip94: Option, +} + +impl BlobDescriptor { + pub fn from_upload(settings: &Settings, value: &FileUpload) -> Self { + let id_hex = hex::encode(&value.id); + Self { + url: format!("{}/{}", settings.public_url, &id_hex), + sha256: id_hex, + size: value.size, + mime_type: Some(value.mime_type.clone()), + created: value.created.timestamp() as u64, + nip94: Some(Nip94Event::from_upload(settings, value)), + } + } +} + #[derive(Serialize, Deserialize)] struct BlossomError { pub message: String, } pub fn blossom_routes() -> Vec { - routes![delete_blob, upload, list_files] + routes![delete_blob, upload, list_files, upload_head] } impl BlossomError { @@ -53,6 +80,26 @@ impl BlossomResponse { } } +struct BlossomHead { + pub msg: Option<&'static str>, +} + +impl<'r> Responder<'r, 'static> for BlossomHead { + fn respond_to(self, _request: &'r Request<'_>) -> rocket::response::Result<'static> { + let mut response = Response::new(); + match self.msg { + Some(m) => { + response.set_status(Status::InternalServerError); + response.set_header(Header::new("x-upload-message", m)); + } + None => { + response.set_status(Status::Ok); + } + } + Ok(response) + } +} + fn check_method(event: &nostr::Event, method: &str) -> bool { if let Some(t) = event.tags.iter().find_map(|t| { if t.kind() == TagKind::Method @@ -103,7 +150,7 @@ async fn upload( }); let size = auth.event.tags.iter().find_map(|t| { if t.kind() == TagKind::Size { - t.content().and_then(|v| v.parse::().ok()) + t.content().and_then(|v| v.parse::().ok()) } else { None } @@ -171,8 +218,8 @@ async fn upload( BlossomResponse::error(format!("Error saving file (db): {}", e)) } else { BlossomResponse::BlobDescriptor(Json(BlobDescriptor::from_upload( - &blob.upload, - &settings.public_url, + &settings, + &blob.upload ))) } } @@ -198,9 +245,58 @@ async fn list_files( Ok((files, _count)) => BlossomResponse::BlobDescriptorList(Json( files .iter() - .map(|f| BlobDescriptor::from_upload(f, &settings.public_url)) + .map(|f| BlobDescriptor::from_upload(&settings, f)) .collect(), )), Err(e) => BlossomResponse::error(format!("Could not list files: {}", e)), } } + +#[rocket::head("/upload")] +async fn upload_head( + auth: BlossomAuth, + settings: &State, +) -> BlossomHead { + if !check_method(&auth.event, "upload") { + return BlossomHead { + msg: Some("Invalid auth method tag") + }; + } + + if let Some(z) = auth.x_content_length { + if z > settings.max_upload_bytes { + return BlossomHead { + msg: Some("File too large") + }; + } + } else { + return BlossomHead { + msg: Some("Missing x-content-length header") + }; + } + + if let None = auth.x_sha_256 { + return BlossomHead { + msg: Some("Missing x-sha-256 header") + }; + } + + if let None = auth.x_content_type { + return BlossomHead { + msg: Some("Missing x-content-type header") + }; + } + + // check whitelist + if let Some(wl) = &settings.whitelist { + if !wl.contains(&auth.event.pubkey.to_hex()) { + return BlossomHead { + msg: Some("Not on whitelist") + }; + } + } + + BlossomHead { + msg: None + } +} \ No newline at end of file diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 60fa205..8a914c2 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -2,19 +2,20 @@ use std::fs; use std::fs::File; use std::str::FromStr; -use anyhow::Error; -use nostr::Event; -use rocket::{Request, State}; -use rocket::fs::NamedFile; -use rocket::http::{ContentType, Header, Status}; -use rocket::response::Responder; - use crate::db::{Database, FileUpload}; use crate::filesystem::FileStore; #[cfg(feature = "blossom")] pub use crate::routes::blossom::blossom_routes; #[cfg(feature = "nip96")] pub use crate::routes::nip96::nip96_routes; +use crate::settings::Settings; +use anyhow::Error; +use nostr::Event; +use rocket::fs::NamedFile; +use rocket::http::{ContentType, Header, Status}; +use rocket::response::Responder; +use rocket::serde::Serialize; +use rocket::{Request, State}; #[cfg(feature = "blossom")] mod blossom; @@ -26,6 +27,51 @@ pub struct FilePayload { pub info: FileUpload, } +#[derive(Clone, Debug, Serialize, Default)] +#[serde(crate = "rocket::serde")] +struct Nip94Event { + pub created_at: i64, + pub content: String, + pub tags: Vec>, +} + +impl Nip94Event { + pub fn from_upload(settings: &Settings, upload: &FileUpload) -> Self { + let hex_id = hex::encode(&upload.id); + let mut tags = vec![ + vec![ + "url".to_string(), + format!("{}/{}", &settings.public_url, &hex_id), + ], + vec!["x".to_string(), hex_id], + vec!["m".to_string(), upload.mime_type.clone()], + vec!["size".to_string(), upload.size.to_string()], + ]; + if let Some(bh) = &upload.blur_hash { + tags.push(vec!["blurhash".to_string(), bh.clone()]); + } + if let (Some(w), Some(h)) = (upload.width, upload.height) { + tags.push(vec!["dim".to_string(), format!("{}x{}", w, h)]) + } + #[cfg(feature = "labels")] + for l in &upload.labels { + let val = if l.label.contains(',') { + let split_val: Vec<&str> = l.label.split(',').collect(); + split_val[0].to_string() + } else { + l.label.clone() + }; + tags.push(vec!["t".to_string(), val]) + } + + Self { + content: upload.name.clone(), + created_at: upload.created.timestamp(), + tags, + } + } +} + impl<'r> Responder<'r, 'static> for FilePayload { fn respond_to(self, request: &'r Request<'_>) -> rocket::response::Result<'static> { let mut response = self.file.respond_to(request)?; diff --git a/src/routes/nip96.rs b/src/routes/nip96.rs index 89bf88f..7fb759c 100644 --- a/src/routes/nip96.rs +++ b/src/routes/nip96.rs @@ -15,7 +15,7 @@ use rocket::{routes, FromForm, Responder, Route, State}; use crate::auth::nip98::Nip98Auth; use crate::db::{Database, FileUpload}; use crate::filesystem::FileStore; -use crate::routes::delete_file; +use crate::routes::{delete_file, Nip94Event}; use crate::settings::Settings; use crate::webhook::Webhook; @@ -46,7 +46,7 @@ struct Nip96Plan { /// landing page for this plan #[serde(skip_serializing_if = "Option::is_none")] pub url: Option, - pub max_byte_size: usize, + pub max_byte_size: u64, /// Range in days / 0 for no expiration /// [7, 0] means it may vary from 7 days to unlimited persistence, /// [0, 0] means it has no expiration @@ -119,58 +119,19 @@ struct Nip96UploadResult { impl Nip96UploadResult { pub fn from_upload(settings: &Settings, upload: &FileUpload) -> Self { - let hex_id = hex::encode(&upload.id); - let mut tags = vec![ - vec![ - "url".to_string(), - format!("{}/{}", &settings.public_url, &hex_id), - ], - vec!["x".to_string(), hex_id], - vec!["m".to_string(), upload.mime_type.clone()], - vec!["size".to_string(), upload.size.to_string()], - ]; - if let Some(bh) = &upload.blur_hash { - tags.push(vec!["blurhash".to_string(), bh.clone()]); - } - if let (Some(w), Some(h)) = (upload.width, upload.height) { - tags.push(vec!["dim".to_string(), format!("{}x{}", w, h)]) - } - #[cfg(feature = "labels")] - for l in &upload.labels { - let val = if l.label.contains(',') { - let split_val: Vec<&str> = l.label.split(',').collect(); - split_val[0].to_string() - } else { - l.label.clone() - }; - tags.push(vec!["t".to_string(), val]) - } - Self { status: "success".to_string(), - nip94_event: Some(Nip94Event { - content: upload.name.clone(), - created_at: upload.created.timestamp(), - tags, - }), + nip94_event: Some(Nip94Event::from_upload(settings, upload)), ..Default::default() } } } -#[derive(Serialize, Default)] -#[serde(crate = "rocket::serde")] -struct Nip94Event { - pub created_at: i64, - pub content: String, - pub tags: Vec>, -} - #[derive(FromForm)] struct Nip96Form<'r> { file: TempFile<'r>, expiration: Option, - size: usize, + size: u64, alt: Option<&'r str>, caption: Option<&'r str>, media_type: Option<&'r str>, @@ -234,7 +195,7 @@ async fn upload( } // account for upload speeds as slow as 1MB/s (8 Mbps) - let mbs = form.size / 1.megabytes().as_u64() as usize; + let mbs = form.size / 1.megabytes().as_u64(); let max_time = 60.max(mbs) as u64; if auth.event.created_at < Timestamp::now().sub(Duration::from_secs(max_time)) { return Nip96Response::error("Auth event timestamp out of range"); diff --git a/src/settings.rs b/src/settings.rs index 33cc67d..8a242c8 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -13,7 +13,7 @@ pub struct Settings { pub database: String, /// Maximum support filesize for uploading - pub max_upload_bytes: usize, + pub max_upload_bytes: u64, /// Public facing url pub public_url: String, diff --git a/ui/index.html b/ui/index.html index ad8de90..abab362 100644 --- a/ui/index.html +++ b/ui/index.html @@ -2,7 +2,7 @@ - void_cat_rs + route96