parent
79338256df
commit
1a1d85b898
1067
Cargo.lock
generated
1067
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
25
Cargo.toml
25
Cargo.toml
@ -1,35 +1,38 @@
|
||||
[package]
|
||||
name = "void_cat"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
labels = ["dep:candle-core", "dep:candle-nn", "dep:candle-transformers"]
|
||||
default = ["nip96", "blossom"]
|
||||
labels = ["nip96", "dep:candle-core", "dep:candle-nn", "dep:candle-transformers"]
|
||||
nip96 = ["dep:ffmpeg-sys-the-third", "dep:blurhash", "dep:libc"]
|
||||
blossom = []
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.21"
|
||||
nostr = "0.30.0"
|
||||
nostr = "0.34.1"
|
||||
pretty_env_logger = "0.5.0"
|
||||
rocket = { version = "0.5.0", features = ["json"] }
|
||||
tokio = { version = "1.37.0", features = ["rt", "rt-multi-thread", "macros"] }
|
||||
base64 = "0.21.7"
|
||||
base64 = "0.22.1"
|
||||
hex = "0.4.3"
|
||||
serde = { version = "1.0.198", features = ["derive"] }
|
||||
uuid = { version = "1.8.0", features = ["v4"] }
|
||||
anyhow = "1.0.82"
|
||||
sha2 = "0.10.8"
|
||||
sqlx = { version = "0.7.4", features = ["mysql", "runtime-tokio", "chrono"] }
|
||||
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"] }
|
||||
libc = "0.2.153"
|
||||
blurhash = "0.2.1"
|
||||
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"] }
|
||||
candle-core = { git = "https://github.com/huggingface/candle.git", version = "0.5.1", optional = true }
|
||||
candle-nn = { git = "https://github.com/huggingface/candle.git", version = "0.5.1", optional = true }
|
||||
candle-transformers = { git = "https://github.com/huggingface/candle.git", version = "0.5.1", 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 }
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use base64::prelude::*;
|
||||
use log::info;
|
||||
use nostr::{Event, JsonUtil, Kind, Tag, Timestamp};
|
||||
use nostr::{Event, JsonUtil, Kind, Tag, TagKind, Timestamp};
|
||||
use rocket::http::Status;
|
||||
use rocket::request::{FromRequest, Outcome};
|
||||
use rocket::{async_trait, Request};
|
||||
@ -15,7 +15,7 @@ impl<'r> FromRequest<'r> for BlossomAuth {
|
||||
type Error = &'static str;
|
||||
|
||||
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
||||
return if let Some(auth) = request.headers().get_one("authorization") {
|
||||
if let Some(auth) = request.headers().get_one("authorization") {
|
||||
if auth.starts_with("Nostr ") {
|
||||
let event = if let Ok(j) = BASE64_STANDARD.decode(auth[6..].to_string()) {
|
||||
if let Ok(ev) = Event::from_json(j) {
|
||||
@ -38,11 +38,13 @@ impl<'r> FromRequest<'r> for BlossomAuth {
|
||||
}
|
||||
|
||||
// check expiration tag
|
||||
if let Some(expiration) = event.tags.iter().find_map(|t| match t {
|
||||
Tag::Expiration(v) => Some(v),
|
||||
_ => None,
|
||||
if let Some(expiration) = event.tags.iter().find_map(|t| if t.kind() == TagKind::Expiration {
|
||||
t.content()
|
||||
} else {
|
||||
None
|
||||
}) {
|
||||
if *expiration <= Timestamp::now() {
|
||||
let u_exp: Timestamp = expiration.parse().unwrap();
|
||||
if u_exp <= Timestamp::now() {
|
||||
return Outcome::Error((Status::new(401), "Expiration invalid"));
|
||||
}
|
||||
} else {
|
||||
@ -69,6 +71,6 @@ impl<'r> FromRequest<'r> for BlossomAuth {
|
||||
}
|
||||
} else {
|
||||
Outcome::Error((Status::new(403), "Auth header not found"))
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ use tokio::io::{AsyncRead, AsyncReadExt, AsyncSeekExt};
|
||||
#[cfg(feature = "labels")]
|
||||
use crate::db::FileLabel;
|
||||
use crate::db::FileUpload;
|
||||
#[cfg(feature = "nip96")]
|
||||
use crate::processing::{compress_file, FileProcessorResult, probe_file, ProbeStream};
|
||||
#[cfg(feature = "labels")]
|
||||
use crate::processing::labeling::label_frame;
|
||||
@ -86,6 +87,7 @@ impl FileStore {
|
||||
|
||||
info!("File saved to temp path: {}", tmp_path.to_str().unwrap());
|
||||
|
||||
#[cfg(feature = "nip96")]
|
||||
if compress {
|
||||
let start = SystemTime::now();
|
||||
let proc_result = compress_file(tmp_path.clone(), mime_type)?;
|
||||
|
28
src/main.rs
28
src/main.rs
@ -20,9 +20,10 @@ mod blob;
|
||||
mod cors;
|
||||
mod db;
|
||||
mod filesystem;
|
||||
#[cfg(feature = "nip96")]
|
||||
mod processing;
|
||||
mod routes;
|
||||
mod settings;
|
||||
mod processing;
|
||||
mod webhook;
|
||||
|
||||
#[rocket::main]
|
||||
@ -44,7 +45,7 @@ async fn main() -> Result<(), Error> {
|
||||
let mut config = rocket::Config::default();
|
||||
let ip: SocketAddr = match &settings.listen {
|
||||
Some(i) => i.parse().unwrap(),
|
||||
None => SocketAddr::new(IpAddr::from([0, 0, 0, 0]), 8000)
|
||||
None => SocketAddr::new(IpAddr::from([0, 0, 0, 0]), 8000),
|
||||
};
|
||||
config.address = ip.ip();
|
||||
config.port = ip.port();
|
||||
@ -56,20 +57,27 @@ async fn main() -> Result<(), Error> {
|
||||
.limit("form", upload_limit);
|
||||
config.ident = Ident::try_new("void-cat-rs").unwrap();
|
||||
|
||||
let rocket = rocket::Rocket::custom(config)
|
||||
let mut rocket = rocket::Rocket::custom(config)
|
||||
.manage(FileStore::new(settings.clone()))
|
||||
.manage(settings.clone())
|
||||
.manage(db.clone())
|
||||
.manage(settings.webhook_url.as_ref().map(|w| Webhook::new(w.clone())))
|
||||
.manage(
|
||||
settings
|
||||
.webhook_url
|
||||
.as_ref()
|
||||
.map(|w| Webhook::new(w.clone())),
|
||||
)
|
||||
.attach(CORS)
|
||||
.attach(Shield::new()) // disable
|
||||
.mount("/", routes::blossom_routes())
|
||||
.mount("/", routes::nip96_routes())
|
||||
.mount("/", routes![root, get_blob, head_blob])
|
||||
.launch()
|
||||
.await;
|
||||
.mount("/", routes![root, get_blob, head_blob]);
|
||||
|
||||
if let Err(e) = rocket {
|
||||
#[cfg(feature = "blossom")] {
|
||||
rocket = rocket.mount("/", routes::blossom_routes());
|
||||
}
|
||||
#[cfg(feature = "nip96")] {
|
||||
rocket = rocket.mount("/", routes::nip96_routes());
|
||||
}
|
||||
if let Err(e) = rocket.launch().await {
|
||||
error!("Rocker error {}", e);
|
||||
Err(Error::from(e))
|
||||
} else {
|
||||
|
@ -4,8 +4,8 @@ use std::ptr;
|
||||
|
||||
use anyhow::Error;
|
||||
use ffmpeg_sys_the_third::{av_frame_alloc, AVFrame, AVPixelFormat, sws_freeContext, sws_getContext, sws_scale_frame};
|
||||
use crate::processing::probe::FFProbe;
|
||||
|
||||
use crate::processing::probe::FFProbe;
|
||||
use crate::processing::webp::WebpProcessor;
|
||||
|
||||
mod webp;
|
||||
|
@ -2,17 +2,17 @@ use std::fs;
|
||||
|
||||
use log::error;
|
||||
use nostr::prelude::hex;
|
||||
use nostr::Tag;
|
||||
use rocket::{Data, Route, routes, State};
|
||||
use nostr::{Tag, TagKind};
|
||||
use rocket::data::ByteUnit;
|
||||
use rocket::http::Status;
|
||||
use rocket::response::Responder;
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::{routes, Data, Route, State};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::auth::blossom::BlossomAuth;
|
||||
use crate::blob::BlobDescriptor;
|
||||
use crate::db::{Database};
|
||||
use crate::db::Database;
|
||||
use crate::filesystem::FileStore;
|
||||
use crate::routes::delete_file;
|
||||
use crate::settings::Settings;
|
||||
@ -54,9 +54,10 @@ impl BlossomResponse {
|
||||
}
|
||||
|
||||
fn check_method(event: &nostr::Event, method: &str) -> bool {
|
||||
if let Some(t) = event.tags.iter().find_map(|t| match t {
|
||||
Tag::Hashtag(tag) => Some(tag),
|
||||
_ => None,
|
||||
if let Some(t) = event.tags.iter().find_map(|t| if t.kind() == TagKind::Method {
|
||||
t.content()
|
||||
} else {
|
||||
None
|
||||
}) {
|
||||
return t == method;
|
||||
}
|
||||
@ -89,10 +90,11 @@ async fn upload(
|
||||
return BlossomResponse::error("Invalid request method tag");
|
||||
}
|
||||
|
||||
let name = auth.event.tags.iter().find_map(|t| match t {
|
||||
Tag::Name(s) => Some(s.clone()),
|
||||
_ => None,
|
||||
});
|
||||
let name = auth
|
||||
.event
|
||||
.tags
|
||||
.iter()
|
||||
.find_map(|t| if t.kind() == TagKind::Name { t.content() } else { None });
|
||||
let size = match auth.event.tags.iter().find_map(|t| {
|
||||
let values = t.as_vec();
|
||||
if values.len() == 2 && values[0] == "size" {
|
||||
@ -102,7 +104,7 @@ async fn upload(
|
||||
}
|
||||
}) {
|
||||
Some(s) => s,
|
||||
None => return BlossomResponse::error("Invalid request, no size tag")
|
||||
None => return BlossomResponse::error("Invalid request, no size tag"),
|
||||
};
|
||||
if size > settings.max_upload_bytes {
|
||||
return BlossomResponse::error("File too large");
|
||||
@ -118,22 +120,31 @@ async fn upload(
|
||||
}
|
||||
}
|
||||
match fs
|
||||
.put(data.open(ByteUnit::from(settings.max_upload_bytes)), &mime_type, false)
|
||||
.put(
|
||||
data.open(ByteUnit::from(settings.max_upload_bytes)),
|
||||
&mime_type,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(mut blob) => {
|
||||
blob.upload.name = name.unwrap_or("".to_string());
|
||||
blob.upload.name = name.unwrap_or("").to_owned();
|
||||
|
||||
let pubkey_vec = auth.event.pubkey.to_bytes().to_vec();
|
||||
if let Some(wh) = webhook.as_ref() {
|
||||
match wh.store_file(&pubkey_vec, blob.clone()) {
|
||||
Ok(store) => if !store {
|
||||
Ok(store) => {
|
||||
if !store {
|
||||
let _ = fs::remove_file(blob.path);
|
||||
return BlossomResponse::error("Upload rejected");
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = fs::remove_file(blob.path);
|
||||
return BlossomResponse::error(format!("Internal error, failed to call webhook: {}", e));
|
||||
return BlossomResponse::error(format!(
|
||||
"Internal error, failed to call webhook: {}",
|
||||
e
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,10 +11,14 @@ 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;
|
||||
|
||||
#[cfg(feature = "blossom")]
|
||||
mod blossom;
|
||||
#[cfg(feature = "nip96")]
|
||||
mod nip96;
|
||||
|
||||
pub struct FilePayload {
|
||||
|
Loading…
x
Reference in New Issue
Block a user