about summary refs log tree commit diff
path: root/kittybox-rs/src
diff options
context:
space:
mode:
Diffstat (limited to 'kittybox-rs/src')
-rw-r--r--kittybox-rs/src/frontend/mod.rs4
-rw-r--r--kittybox-rs/src/frontend/onboarding.rs99
-rw-r--r--kittybox-rs/src/indieauth.rs9
-rw-r--r--kittybox-rs/src/main.rs10
-rw-r--r--kittybox-rs/src/media/mod.rs10
-rw-r--r--kittybox-rs/src/media/storage/file.rs14
-rw-r--r--kittybox-rs/src/media/storage/mod.rs13
-rw-r--r--kittybox-rs/src/metrics.rs2
-rw-r--r--kittybox-rs/src/micropub/mod.rs145
9 files changed, 173 insertions, 133 deletions
diff --git a/kittybox-rs/src/frontend/mod.rs b/kittybox-rs/src/frontend/mod.rs
index 51db2e1..bc9925f 100644
--- a/kittybox-rs/src/frontend/mod.rs
+++ b/kittybox-rs/src/frontend/mod.rs
@@ -12,9 +12,7 @@ use tracing::{debug, error};
 //pub mod login;
 pub mod onboarding;
 
-use kittybox_templates::{
-    Entry, ErrorPage, Feed, MainPage, Template, VCard, POSTS_PER_PAGE,
-};
+use kittybox_templates::{Entry, ErrorPage, Feed, MainPage, Template, VCard, POSTS_PER_PAGE};
 
 pub use kittybox_util::IndiewebEndpoints;
 
diff --git a/kittybox-rs/src/frontend/onboarding.rs b/kittybox-rs/src/frontend/onboarding.rs
index 18def1d..9027201 100644
--- a/kittybox-rs/src/frontend/onboarding.rs
+++ b/kittybox-rs/src/frontend/onboarding.rs
@@ -1,25 +1,28 @@
-use kittybox_templates::{ErrorPage, Template, OnboardingPage};
-use crate::database::{Storage, Settings};
+use crate::database::{Settings, Storage};
 use axum::{
-    Json,
-    extract::{Host, Extension},
+    extract::{Extension, Host},
     http::StatusCode,
     response::{Html, IntoResponse},
+    Json,
 };
+use kittybox_templates::{ErrorPage, OnboardingPage, Template};
 use serde::Deserialize;
 use tracing::{debug, error};
 
 use super::FrontendError;
 
 pub async fn get() -> Html<String> {
-    Html(Template {
-        title: "Kittybox - Onboarding",
-        blog_name: "Kittybox",
-        feeds: vec![],
-        endpoints: None,
-        user: None,
-        content: OnboardingPage {}.to_string()
-    }.to_string())
+    Html(
+        Template {
+            title: "Kittybox - Onboarding",
+            blog_name: "Kittybox",
+            feeds: vec![],
+            endpoints: None,
+            user: None,
+            content: OnboardingPage {}.to_string(),
+        }
+        .to_string(),
+    )
 }
 
 #[derive(Deserialize, Debug)]
@@ -45,21 +48,21 @@ impl OnboardingData {
 
 #[tracing::instrument(skip(db, http))]
 async fn onboard<D: Storage + 'static>(
-    db: D, user_uid: url::Url, data: OnboardingData, http: reqwest::Client
+    db: D,
+    user_uid: url::Url,
+    data: OnboardingData,
+    http: reqwest::Client,
 ) -> Result<(), FrontendError> {
     // Create a user to pass to the backend
     // At this point the site belongs to nobody, so it is safe to do
-    let user = crate::indieauth::User::new(
-        user_uid.as_str(),
-        "https://kittybox.fireburn.ru/",
-        "create"
-    );
+    let user =
+        crate::indieauth::User::new(user_uid.as_str(), "https://kittybox.fireburn.ru/", "create");
 
     if data.user["type"][0] != "h-card" || data.first_post["type"][0] != "h-entry" {
         return Err(FrontendError::with_code(
             StatusCode::BAD_REQUEST,
-            "user and first_post should be an h-card and an h-entry"
-        ))
+            "user and first_post should be an h-card and an h-entry",
+        ));
     }
 
     db.set_setting(Settings::SiteName, user.me.as_str(), &data.blog_name)
@@ -71,7 +74,9 @@ async fn onboard<D: Storage + 'static>(
         hcard["properties"]["uid"] = serde_json::json!([&user_uid]);
         crate::micropub::normalize_mf2(hcard, &user)
     };
-    db.put_post(&hcard, user_uid.as_str()).await.map_err(FrontendError::from)?;
+    db.put_post(&hcard, user_uid.as_str())
+        .await
+        .map_err(FrontendError::from)?;
 
     debug!("Creating feeds...");
     for feed in data.feeds {
@@ -87,16 +92,18 @@ async fn onboard<D: Storage + 'static>(
             &user,
         );
 
-        db.put_post(&feed, user_uid.as_str()).await.map_err(FrontendError::from)?;
+        db.put_post(&feed, user_uid.as_str())
+            .await
+            .map_err(FrontendError::from)?;
     }
     let (uid, post) = crate::micropub::normalize_mf2(data.first_post, &user);
-    crate::micropub::_post(user, uid, post, db, http).await.map_err(|e| {
-        FrontendError {
+    crate::micropub::_post(user, uid, post, db, http)
+        .await
+        .map_err(|e| FrontendError {
             msg: "Error while posting the first post".to_string(),
             source: Some(Box::new(e)),
-            code: StatusCode::INTERNAL_SERVER_ERROR
-        }
-    })?;
+            code: StatusCode::INTERNAL_SERVER_ERROR,
+        })?;
 
     Ok(())
 }
@@ -105,36 +112,34 @@ pub async fn post<D: Storage + 'static>(
     Extension(db): Extension<D>,
     Host(host): Host,
     Json(data): Json<OnboardingData>,
-    Extension(http): Extension<reqwest::Client>
+    Extension(http): Extension<reqwest::Client>,
 ) -> axum::response::Response {
     let user_uid = format!("https://{}/", host.as_str());
 
     if db.post_exists(&user_uid).await.unwrap() {
-        IntoResponse::into_response((
-            StatusCode::FOUND,
-            [("Location", "/")]
-        ))
+        IntoResponse::into_response((StatusCode::FOUND, [("Location", "/")]))
     } else {
         match onboard(db, user_uid.parse().unwrap(), data, http).await {
-            Ok(()) => IntoResponse::into_response((
-                StatusCode::FOUND,
-                [("Location", "/")]
-            )),
+            Ok(()) => IntoResponse::into_response((StatusCode::FOUND, [("Location", "/")])),
             Err(err) => {
                 error!("Onboarding error: {}", err);
                 IntoResponse::into_response((
                     err.code(),
-                    Html(Template {
-                        title: "Kittybox - Onboarding",
-                        blog_name: "Kittybox",
-                        feeds: vec![],
-                        endpoints: None,
-                        user: None,
-                        content: ErrorPage {
-                            code: err.code(),
-                            msg: Some(err.msg().to_string()),
-                        }.to_string(),
-                    }.to_string())
+                    Html(
+                        Template {
+                            title: "Kittybox - Onboarding",
+                            blog_name: "Kittybox",
+                            feeds: vec![],
+                            endpoints: None,
+                            user: None,
+                            content: ErrorPage {
+                                code: err.code(),
+                                msg: Some(err.msg().to_string()),
+                            }
+                            .to_string(),
+                        }
+                        .to_string(),
+                    ),
                 ))
             }
         }
diff --git a/kittybox-rs/src/indieauth.rs b/kittybox-rs/src/indieauth.rs
index 63de859..103f514 100644
--- a/kittybox-rs/src/indieauth.rs
+++ b/kittybox-rs/src/indieauth.rs
@@ -151,7 +151,10 @@ where
 {
     type Rejection = IndieAuthError;
 
-    #[cfg_attr(all(debug_assertions, not(test)), allow(unreachable_code, unused_variables))]
+    #[cfg_attr(
+        all(debug_assertions, not(test)),
+        allow(unreachable_code, unused_variables)
+    )]
     async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
         // Return a fake user if we're running a debug build
         // I don't wanna bother with authentication
@@ -159,7 +162,7 @@ where
         return Ok(User::new(
             "http://localhost:8080/",
             "https://quill.p3k.io/",
-            "create update delete media"
+            "create update delete media",
         ));
 
         let TypedHeader(Authorization(token)) =
@@ -232,7 +235,7 @@ mod tests {
     use super::User;
     use axum::{
         extract::FromRequest,
-        http::{Method, Request}
+        http::{Method, Request},
     };
     use httpmock::prelude::*;
 
diff --git a/kittybox-rs/src/main.rs b/kittybox-rs/src/main.rs
index ef051ba..ece31df 100644
--- a/kittybox-rs/src/main.rs
+++ b/kittybox-rs/src/main.rs
@@ -197,15 +197,17 @@ async fn main() {
             .route(
                 "/.kittybox/onboarding",
                 axum::routing::get(kittybox::frontend::onboarding::get)
-                    .post(kittybox::frontend::onboarding::post::<FileStorage>)
+                    .post(kittybox::frontend::onboarding::post::<FileStorage>),
             )
             .route(
                 "/.kittybox/micropub",
                 axum::routing::get(kittybox::micropub::query::<FileStorage>)
                     .post(kittybox::micropub::post::<FileStorage>)
-                    .layer(tower_http::cors::CorsLayer::new()
-                           .allow_methods([axum::http::Method::GET, axum::http::Method::POST])
-                           .allow_origin(tower_http::cors::Any)),
+                    .layer(
+                        tower_http::cors::CorsLayer::new()
+                            .allow_methods([axum::http::Method::GET, axum::http::Method::POST])
+                            .allow_origin(tower_http::cors::Any),
+                    ),
             )
             .route(
                 "/.kittybox/micropub/client",
diff --git a/kittybox-rs/src/media/mod.rs b/kittybox-rs/src/media/mod.rs
index d18cf34..0d26f92 100644
--- a/kittybox-rs/src/media/mod.rs
+++ b/kittybox-rs/src/media/mod.rs
@@ -1,9 +1,9 @@
-use bytes::buf::Buf;
-use futures_util::StreamExt;
 use axum::{
-    extract::{Host, Extension, Multipart},
-    response::{Response, IntoResponse, Json}
+    extract::{Extension, Host, Multipart},
+    response::{IntoResponse, Json, Response},
 };
+use bytes::buf::Buf;
+use futures_util::StreamExt;
 
 pub mod storage;
 use storage::{MediaStore, MediaStoreError};
@@ -36,7 +36,7 @@ use storage::{MediaStore, MediaStoreError};
 pub async fn upload<S: MediaStore>(
     Host(host): Host,
     upload: Multipart,
-    Extension(db): Extension<S>
+    Extension(db): Extension<S>,
 ) -> Response {
     todo!()
 }
diff --git a/kittybox-rs/src/media/storage/file.rs b/kittybox-rs/src/media/storage/file.rs
index 8c0ddf0..ea1f010 100644
--- a/kittybox-rs/src/media/storage/file.rs
+++ b/kittybox-rs/src/media/storage/file.rs
@@ -22,16 +22,22 @@ impl From<tokio::io::Error> for MediaStoreError {
 #[async_trait]
 impl MediaStore for FileStore {
     async fn write_streaming(
-        &self, domain: url::Host, filename: &str,
-        content: axum::extract::multipart::Field<'_>
+        &self,
+        domain: url::Host,
+        filename: &str,
+        content: axum::extract::multipart::Field<'_>,
     ) -> Result<()> {
         todo!()
     }
 
-    async fn read_streaming(&self, domain: url::Host, filename: &str) -> Result<tokio_util::io::ReaderStream<Box<dyn tokio::io::AsyncRead>>> {
+    async fn read_streaming(
+        &self,
+        domain: url::Host,
+        filename: &str,
+    ) -> Result<tokio_util::io::ReaderStream<Box<dyn tokio::io::AsyncRead>>> {
         todo!()
     }
-    
+
     async fn write(&self, domain: url::Host, filename: &str, content: &[u8]) -> Result<()> {
         let path = self.base.join(format!("{}/{}", domain, filename));
 
diff --git a/kittybox-rs/src/media/storage/mod.rs b/kittybox-rs/src/media/storage/mod.rs
index e9b01f9..ba880ab 100644
--- a/kittybox-rs/src/media/storage/mod.rs
+++ b/kittybox-rs/src/media/storage/mod.rs
@@ -45,9 +45,18 @@ pub type Result<T> = std::result::Result<T, MediaStoreError>;
 
 #[async_trait]
 pub trait MediaStore: 'static + Send + Sync + Clone {
-    async fn write_streaming(&self, domain: url::Host, filename: &str, content: axum::extract::multipart::Field<'_>) -> Result<()>;
+    async fn write_streaming(
+        &self,
+        domain: url::Host,
+        filename: &str,
+        content: axum::extract::multipart::Field<'_>,
+    ) -> Result<()>;
     async fn write(&self, domain: url::Host, filename: &str, content: &[u8]) -> Result<()>;
-    async fn read_streaming(&self, domain: url::Host, filename: &str) -> Result<tokio_util::io::ReaderStream<Box<dyn tokio::io::AsyncRead>>>;
+    async fn read_streaming(
+        &self,
+        domain: url::Host,
+        filename: &str,
+    ) -> Result<tokio_util::io::ReaderStream<Box<dyn tokio::io::AsyncRead>>>;
     async fn read(&self, domain: url::Host, filename: &str) -> Result<Vec<u8>>;
     async fn delete(&self, domain: url::Host, filename: &str) -> Result<()>;
 }
diff --git a/kittybox-rs/src/metrics.rs b/kittybox-rs/src/metrics.rs
index 48f5d9b..e13fcb9 100644
--- a/kittybox-rs/src/metrics.rs
+++ b/kittybox-rs/src/metrics.rs
@@ -1,8 +1,8 @@
 #![allow(unused_imports, dead_code)]
 use async_trait::async_trait;
 use lazy_static::lazy_static;
-use std::time::{Duration, Instant};
 use prometheus::Encoder;
+use std::time::{Duration, Instant};
 
 // TODO: Vendor in the Metrics struct from warp_prometheus and rework the path matching algorithm
 
diff --git a/kittybox-rs/src/micropub/mod.rs b/kittybox-rs/src/micropub/mod.rs
index d7be785..d8a84e6 100644
--- a/kittybox-rs/src/micropub/mod.rs
+++ b/kittybox-rs/src/micropub/mod.rs
@@ -1,15 +1,15 @@
 use crate::database::{MicropubChannel, Storage, StorageError};
 use crate::indieauth::User;
 use crate::micropub::util::form_to_mf2_json;
-use axum::TypedHeader;
 use axum::extract::{BodyStream, Query};
 use axum::headers::ContentType;
 use axum::response::{IntoResponse, Response};
+use axum::TypedHeader;
 use axum::{http::StatusCode, Extension};
-use tracing::{error, info, warn, debug};
 use serde::{Deserialize, Serialize};
 use serde_json::json;
 use std::fmt::Display;
+use tracing::{debug, error, info, warn};
 
 #[derive(Serialize, Deserialize, Debug, PartialEq)]
 #[serde(rename_all = "kebab-case")]
@@ -91,7 +91,7 @@ impl axum::response::IntoResponse for MicropubError {
     fn into_response(self) -> axum::response::Response {
         axum::response::IntoResponse::into_response((
             StatusCode::from(&self),
-            axum::response::Json(self)
+            axum::response::Json(self),
         ))
     }
 }
@@ -374,11 +374,8 @@ pub(crate) async fn _post<D: 'static + Storage>(
         }
     }
 
-    let reply = IntoResponse::into_response((
-        StatusCode::ACCEPTED,
-        [("Location", uid.as_str())],
-        ()
-    ));
+    let reply =
+        IntoResponse::into_response((StatusCode::ACCEPTED, [("Location", uid.as_str())], ()));
 
     tokio::task::spawn(background_processing(db, mf2, http));
 
@@ -486,11 +483,14 @@ async fn post_action<D: Storage>(
 
 enum PostBody {
     Action(MicropubAction),
-    MF2(serde_json::Value)
+    MF2(serde_json::Value),
 }
 
 #[tracing::instrument]
-async fn dispatch_body(mut body: BodyStream, content_type: ContentType) -> Result<PostBody, MicropubError> {
+async fn dispatch_body(
+    mut body: BodyStream,
+    content_type: ContentType,
+) -> Result<PostBody, MicropubError> {
     let body: Vec<u8> = {
         debug!("Buffering body...");
         use tokio_stream::StreamExt;
@@ -538,7 +538,7 @@ async fn dispatch_body(mut body: BodyStream, content_type: ContentType) -> Resul
     } else {
         Err(MicropubError::new(
             ErrorType::UnsupportedMediaType,
-            "This Content-Type is not recognized. Try application/json instead?"
+            "This Content-Type is not recognized. Try application/json instead?",
         ))
     }
 }
@@ -549,28 +549,28 @@ pub async fn post<D: Storage + 'static>(
     Extension(http): Extension<reqwest::Client>,
     user: User,
     body: BodyStream,
-    TypedHeader(content_type): TypedHeader<ContentType>
+    TypedHeader(content_type): TypedHeader<ContentType>,
 ) -> axum::response::Response {
     match dispatch_body(body, content_type).await {
         Ok(PostBody::Action(action)) => match post_action(action, db, user).await {
             Ok(()) => Response::default(),
-            Err(err) => err.into_response()
+            Err(err) => err.into_response(),
         },
         Ok(PostBody::MF2(mf2)) => {
             let (uid, mf2) = normalize_mf2(mf2, &user);
             match _post(user, uid, mf2, db, http).await {
                 Ok(response) => response,
-                Err(err) => err.into_response()
+                Err(err) => err.into_response(),
             }
-        },
-        Err(err) => err.into_response()
+        }
+        Err(err) => err.into_response(),
     }
 }
 
 pub async fn query<D: Storage>(
     Extension(db): Extension<D>,
     Query(query): Query<MicropubQuery>,
-    user: User
+    user: User,
 ) -> axum::response::Response {
     let host = axum::http::Uri::try_from(user.me.as_str())
         .unwrap()
@@ -582,10 +582,13 @@ pub async fn query<D: Storage>(
         QueryType::Config => {
             let channels: Vec<MicropubChannel> = match db.get_channels(host.as_str()).await {
                 Ok(chans) => chans,
-                Err(err) => return MicropubError::new(
-                    ErrorType::InternalServerError,
-                    &format!("Error fetching channels: {}", err)
-                ).into_response(),
+                Err(err) => {
+                    return MicropubError::new(
+                        ErrorType::InternalServerError,
+                        &format!("Error fetching channels: {}", err),
+                    )
+                    .into_response()
+                }
             };
 
             axum::response::Json(json!({
@@ -598,50 +601,59 @@ pub async fn query<D: Storage>(
                 "channels": channels,
                 "_kittybox_authority": host.as_str(),
                 "syndicate-to": []
-            })).into_response()
-        },
+            }))
+            .into_response()
+        }
         QueryType::Source => {
             match query.url {
                 Some(url) => {
-                    if axum::http::Uri::try_from(&url).unwrap().authority().unwrap() != &host {
+                    if axum::http::Uri::try_from(&url)
+                        .unwrap()
+                        .authority()
+                        .unwrap()
+                        != &host
+                    {
                         return MicropubError::new(
                             ErrorType::NotAuthorized,
-                            "You are requesting a post from a website that doesn't belong to you."
-                        ).into_response()
+                            "You are requesting a post from a website that doesn't belong to you.",
+                        )
+                        .into_response();
                     }
                     match db.get_post(&url).await {
                         Ok(some) => match some {
                             Some(post) => axum::response::Json(&post).into_response(),
                             None => MicropubError::new(
                                 ErrorType::NotFound,
-                                "The specified MF2 object was not found in database."
-                            ).into_response()
+                                "The specified MF2 object was not found in database.",
+                            )
+                            .into_response(),
                         },
                         Err(err) => MicropubError::new(
                             ErrorType::InternalServerError,
-                            &format!("Backend error: {}", err)
-                        ).into_response()
+                            &format!("Backend error: {}", err),
+                        )
+                        .into_response(),
                     }
-                },
+                }
                 None => {
                     // Here, one should probably attempt to query at least the main feed and collect posts
                     // Using a pre-made query function can't be done because it does unneeded filtering
                     // Don't implement for now, this is optional
                     MicropubError::new(
                         ErrorType::InvalidRequest,
-                        "Querying for post list is not implemented yet."
-                    ).into_response()
+                        "Querying for post list is not implemented yet.",
+                    )
+                    .into_response()
                 }
             }
-        },
-        QueryType::Channel => {
-            match db.get_channels(host.as_str()).await {
-                Ok(chans) => axum::response::Json(json!({"channels": chans})).into_response(),
-                Err(err) => MicropubError::new(
-                    ErrorType::InternalServerError,
-                    &format!("Error fetching channels: {}", err)
-                ).into_response()
-            }
+        }
+        QueryType::Channel => match db.get_channels(host.as_str()).await {
+            Ok(chans) => axum::response::Json(json!({ "channels": chans })).into_response(),
+            Err(err) => MicropubError::new(
+                ErrorType::InternalServerError,
+                &format!("Error fetching channels: {}", err),
+            )
+            .into_response(),
         },
         QueryType::SyndicateTo => {
             axum::response::Json(json!({ "syndicate-to": [] })).into_response()
@@ -725,16 +737,16 @@ mod tests {
         let user = crate::indieauth::User::new(
             "https://localhost:8080/",
             "https://kittybox.fireburn.ru/",
-            "profile"
+            "profile",
         );
         let (uid, mf2) = super::normalize_mf2(post, &user);
-        
-        let err = super::_post(
-            user, uid, mf2, db.clone(), reqwest::Client::new()
-        ).await.unwrap_err();
+
+        let err = super::_post(user, uid, mf2, db.clone(), reqwest::Client::new())
+            .await
+            .unwrap_err();
 
         assert_eq!(err.error, super::ErrorType::InvalidScope);
-        
+
         let hashmap = db.mapping.read().await;
         assert!(hashmap.is_empty());
     }
@@ -754,21 +766,20 @@ mod tests {
         let user = crate::indieauth::User::new(
             "https://aaronparecki.com/",
             "https://kittybox.fireburn.ru/",
-            "create update media"
+            "create update media",
         );
         let (uid, mf2) = super::normalize_mf2(post, &user);
-        
-        let err = super::_post(
-            user, uid, mf2, db.clone(), reqwest::Client::new()
-        ).await.unwrap_err();
+
+        let err = super::_post(user, uid, mf2, db.clone(), reqwest::Client::new())
+            .await
+            .unwrap_err();
 
         assert_eq!(err.error, super::ErrorType::Forbidden);
-        
+
         let hashmap = db.mapping.read().await;
         assert!(hashmap.is_empty());
     }
 
-    
     #[tokio::test]
     async fn test_post_mf2() {
         let db = crate::database::MemoryStorage::new();
@@ -782,31 +793,37 @@ mod tests {
         let user = crate::indieauth::User::new(
             "https://localhost:8080/",
             "https://kittybox.fireburn.ru/",
-            "create"
+            "create",
         );
         let (uid, mf2) = super::normalize_mf2(post, &user);
 
-        let res = super::_post(
-            user, uid, mf2, db.clone(), reqwest::Client::new()
-        ).await.unwrap();
+        let res = super::_post(user, uid, mf2, db.clone(), reqwest::Client::new())
+            .await
+            .unwrap();
 
         assert!(res.headers().contains_key("Location"));
         let location = res.headers().get("Location").unwrap();
         assert!(db.post_exists(location.to_str().unwrap()).await.unwrap());
-        assert!(db.post_exists("https://localhost:8080/feeds/main").await.unwrap());
+        assert!(db
+            .post_exists("https://localhost:8080/feeds/main")
+            .await
+            .unwrap());
     }
 
     #[tokio::test]
     async fn test_query_foreign_url() {
         let mut res = super::query(
             axum::Extension(crate::database::MemoryStorage::new()),
-            axum::extract::Query(super::MicropubQuery::source("https://aaronparecki.com/feeds/main")),
+            axum::extract::Query(super::MicropubQuery::source(
+                "https://aaronparecki.com/feeds/main",
+            )),
             crate::indieauth::User::new(
                 "https://fireburn.ru/",
                 "https://quill.p3k.io/",
-                "create update media"
-            )
-        ).await;
+                "create update media",
+            ),
+        )
+        .await;
 
         assert_eq!(res.status(), 401);
         let body = res.body_mut().data().await.unwrap().unwrap();