about summary refs log tree commit diff
path: root/kittybox-rs/src/media/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'kittybox-rs/src/media/mod.rs')
-rw-r--r--kittybox-rs/src/media/mod.rs141
1 files changed, 0 insertions, 141 deletions
diff --git a/kittybox-rs/src/media/mod.rs b/kittybox-rs/src/media/mod.rs
deleted file mode 100644
index 71f875e..0000000
--- a/kittybox-rs/src/media/mod.rs
+++ /dev/null
@@ -1,141 +0,0 @@
-use std::convert::TryFrom;
-
-use axum::{
-    extract::{Extension, Host, multipart::Multipart, Path},
-    response::{IntoResponse, Response},
-    headers::{Header, HeaderValue, IfNoneMatch, HeaderMapExt},
-    TypedHeader,
-};
-use kittybox_util::error::{MicropubError, ErrorType};
-use kittybox_indieauth::Scope;
-use crate::indieauth::{User, backend::AuthBackend};
-
-pub mod storage;
-use storage::{MediaStore, MediaStoreError, Metadata, ErrorKind};
-pub use storage::file::FileStore;
-
-impl From<MediaStoreError> for MicropubError {
-    fn from(err: MediaStoreError) -> Self {
-        Self {
-            error: ErrorType::InternalServerError,
-            error_description: format!("{}", err)
-        }
-    }
-}
-
-#[tracing::instrument(skip(blobstore))]
-pub(crate) async fn upload<S: MediaStore, A: AuthBackend>(
-    Extension(blobstore): Extension<S>,
-    user: User<A>,
-    mut upload: Multipart
-) -> Response {
-    if !user.check_scope(&Scope::Media) {
-        return MicropubError {
-            error: ErrorType::NotAuthorized,
-            error_description: "Interacting with the media storage requires the \"media\" scope.".to_owned()
-        }.into_response();
-    }
-    let host = user.me.host().unwrap().to_string() + &user.me.port().map(|i| format!(":{}", i)).unwrap_or_default();
-    let field = match upload.next_field().await {
-        Ok(Some(field)) => field,
-        Ok(None) => {
-            return MicropubError {
-                error: ErrorType::InvalidRequest,
-                error_description: "Send multipart/form-data with one field named file".to_owned()
-            }.into_response();
-        },
-        Err(err) => {
-            return MicropubError {
-                error: ErrorType::InternalServerError,
-                error_description: format!("Error while parsing multipart/form-data: {}", err)
-            }.into_response();
-        },
-    };
-    let metadata: Metadata = (&field).into();
-    match blobstore.write_streaming(&host, metadata, field).await {
-        Ok(filename) => IntoResponse::into_response((
-            axum::http::StatusCode::CREATED,
-            [
-                ("Location", user.me.join(
-                    &format!(".kittybox/media/uploads/{}", filename)
-                ).unwrap().as_str())
-            ]
-        )),
-        Err(err) => MicropubError::from(err).into_response()
-    }
-}
-
-#[tracing::instrument(skip(blobstore))]
-pub(crate) async fn serve<S: MediaStore>(
-    Host(host): Host,
-    Path(path): Path<String>,
-    if_none_match: Option<TypedHeader<IfNoneMatch>>,
-    Extension(blobstore): Extension<S>
-) -> Response {
-    use axum::http::StatusCode;
-    tracing::debug!("Searching for file...");
-    match blobstore.read_streaming(&host, path.as_str()).await {
-        Ok((metadata, stream)) => {
-            tracing::debug!("Metadata: {:?}", metadata);
-
-            let etag = if let Some(etag) = metadata.etag {
-                let etag = format!("\"{}\"", etag).parse::<axum::headers::ETag>().unwrap();
-
-                if let Some(TypedHeader(if_none_match)) = if_none_match {
-                    tracing::debug!("If-None-Match: {:?}", if_none_match);
-                    // If-None-Match is a negative precondition that
-                    // returns 304 when it doesn't match because it
-                    // only matches when file is different
-                    if !if_none_match.precondition_passes(&etag) {
-                        return StatusCode::NOT_MODIFIED.into_response()
-                    }
-                }
-
-                Some(etag)
-            } else { None };
-
-            let mut r = Response::builder();
-            {
-                let headers = r.headers_mut().unwrap();
-                headers.insert(
-                    "Content-Type",
-                    HeaderValue::from_str(
-                        metadata.content_type
-                            .as_deref()
-                            .unwrap_or("application/octet-stream")
-                    ).unwrap()
-                );
-                if let Some(length) = metadata.length {
-                    headers.insert(
-                        "Content-Length",
-                        HeaderValue::from_str(&length.to_string()).unwrap()
-                    );
-                }
-                if let Some(etag) = etag {
-                    headers.typed_insert(etag);
-                }
-            }
-            r.body(axum::body::StreamBody::new(stream))
-                .unwrap()
-                .into_response()
-        },
-        Err(err) => match err.kind() {
-            ErrorKind::NotFound => {
-                IntoResponse::into_response(StatusCode::NOT_FOUND)
-            },
-            _ => {
-                tracing::error!("{}", err);
-                IntoResponse::into_response(StatusCode::INTERNAL_SERVER_ERROR)
-            }
-        }
-    }
-}
-
-#[must_use]
-pub fn router<S: MediaStore, A: AuthBackend>(blobstore: S, auth: A) -> axum::Router {
-    axum::Router::new()
-        .route("/", axum::routing::post(upload::<S, A>))
-        .route("/uploads/*file", axum::routing::get(serve::<S>))
-        .layer(axum::Extension(blobstore))
-        .layer(axum::Extension(auth))
-}