feat: rename / upgrades
All checks were successful
continuous-integration/drone Build is passing

This commit is contained in:
kieran 2024-09-22 14:26:23 +01:00
parent d41d561fdc
commit c8da87e0dd
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
19 changed files with 289 additions and 197 deletions

View File

@ -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)

157
Cargo.lock generated
View File

@ -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",
]

View File

@ -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 }

View File

@ -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"]
ENTRYPOINT ["./bin/route96"]

View File

@ -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

View File

@ -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"

View File

@ -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"
# webhook_url = "https://api.snort.social/api/v1/media/webhook"

View File

@ -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"

View File

@ -5,7 +5,7 @@ services:
image: mariadb
environment:
- "MARIADB_ROOT_PASSWORD=root"
- "MARIADB_DATABASE=void_cat"
- "MARIADB_DATABASE=route96"
ports:
- "3366:3306"
volumes:

View File

@ -7,6 +7,9 @@ use rocket::{async_trait, Request};
pub struct BlossomAuth {
pub content_type: Option<String>,
pub x_content_type: Option<String>,
pub x_sha_256: Option<String>,
pub x_content_length: Option<u64>,
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"))

View File

@ -9,7 +9,7 @@ use rocket::request::{FromRequest, Outcome};
pub struct Nip98Auth {
pub content_type: Option<String>,
pub content_length: Option<usize>,
pub content_length: Option<u64>,
pub event: Event,
}

View File

@ -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<String>,
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,
}
}
}

View File

@ -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()))

View File

@ -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;
}

View File

@ -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<String>,
pub created: u64,
#[serde(rename = "nip94", skip_serializing_if = "Option::is_none")]
pub nip94: Option<Nip94Event>,
}
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<Route> {
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::<usize>().ok())
t.content().and_then(|v| v.parse::<u64>().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<Settings>,
) -> 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
}
}

View File

@ -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<Vec<String>>,
}
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)?;

View File

@ -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<String>,
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<Vec<String>>,
}
#[derive(FromForm)]
struct Nip96Form<'r> {
file: TempFile<'r>,
expiration: Option<usize>,
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");

View File

@ -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,

View File

@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<title>void_cat_rs</title>
<title>route96</title>
<style>
html {
background-color: black;
@ -144,7 +144,7 @@
</head>
<body>
<h1>
Welcome to void_cat_rs
Welcome to route96
</h1>
<div class="flex flex-col gap-2">
<div>