about summary refs log tree commit diff
path: root/src/micropub
diff options
context:
space:
mode:
authorVika <vika@fireburn.ru>2024-08-20 00:35:30 +0300
committerVika <vika@fireburn.ru>2024-08-20 01:46:13 +0300
commitd6a8525274ccd96a5ce9540ea2ba1e463c84ff90 (patch)
tree9daa6fceda3f019629f99acb1b150882a5e151d4 /src/micropub
parenta46e16258f83cc5939c568bba3a62ccc28114365 (diff)
downloadkittybox-d6a8525274ccd96a5ce9540ea2ba1e463c84ff90.tar.zst
kittybox-util: 0.1.0 -> 0.2.0
Micropub types are now more coherent and gathered in one place.
Diffstat (limited to 'src/micropub')
-rw-r--r--src/micropub/mod.rs87
1 files changed, 43 insertions, 44 deletions
diff --git a/src/micropub/mod.rs b/src/micropub/mod.rs
index 63b81c5..65519e4 100644
--- a/src/micropub/mod.rs
+++ b/src/micropub/mod.rs
@@ -18,17 +18,7 @@ use tokio::sync::Mutex;
 use tokio::task::JoinSet;
 use tracing::{debug, error, info, warn};
 use kittybox_indieauth::{Scope, TokenData};
-use kittybox_util::{MicropubError, ErrorType};
-
-#[derive(Serialize, Deserialize, Debug, PartialEq)]
-#[serde(rename_all = "kebab-case")]
-enum QueryType {
-    Source,
-    Config,
-    Channel,
-    SyndicateTo,
-    Category
-}
+use kittybox_util::micropub::{Error as MicropubError, ErrorKind, QueryType};
 
 #[derive(Serialize, Deserialize, Debug)]
 pub struct MicropubQuery {
@@ -40,8 +30,8 @@ impl From<StorageError> for MicropubError {
     fn from(err: StorageError) -> Self {
         Self {
             error: match err.kind() {
-                crate::database::ErrorKind::NotFound => ErrorType::NotFound,
-                _ => ErrorType::InternalServerError,
+                crate::database::ErrorKind::NotFound => ErrorKind::NotFound,
+                _ => ErrorKind::InternalServerError,
             },
             error_description: format!("Backend error: {}", err),
         }
@@ -257,7 +247,7 @@ pub(crate) async fn _post<D: 'static + Storage>(
     // Security check! Do we have an OAuth2 scope to proceed?
     if !user.check_scope(&Scope::Create) {
         return Err(MicropubError {
-            error: ErrorType::InvalidScope,
+            error: ErrorKind::InvalidScope,
             error_description: "Not enough privileges - try acquiring the \"create\" scope."
                 .to_owned(),
         });
@@ -272,7 +262,7 @@ pub(crate) async fn _post<D: 'static + Storage>(
             .any(|url| !url.as_str().unwrap().starts_with(user.me.as_str()))
     {
         return Err(MicropubError {
-            error: ErrorType::Forbidden,
+            error: ErrorKind::Forbidden,
             error_description: "You're posting to a website that's not yours.".to_owned(),
         });
     }
@@ -280,7 +270,7 @@ pub(crate) async fn _post<D: 'static + Storage>(
     // Security check #3! Are we overwriting an existing document?
     if db.post_exists(&uid).await? {
         return Err(MicropubError {
-            error: ErrorType::AlreadyExists,
+            error: ErrorKind::AlreadyExists,
             error_description: "UID clash was detected, operation aborted.".to_owned(),
         });
     }
@@ -399,7 +389,7 @@ async fn post_action<D: Storage, A: AuthBackend>(
         uri
     } else {
         return Err(MicropubError {
-            error: ErrorType::InvalidRequest,
+            error: ErrorKind::InvalidRequest,
             error_description: "Your URL doesn't parse properly.".to_owned(),
         });
     };
@@ -414,7 +404,7 @@ async fn post_action<D: Storage, A: AuthBackend>(
             .unwrap()
     {
         return Err(MicropubError {
-            error: ErrorType::Forbidden,
+            error: ErrorKind::Forbidden,
             error_description: "Don't tamper with others' posts!".to_owned(),
         });
     }
@@ -423,7 +413,7 @@ async fn post_action<D: Storage, A: AuthBackend>(
         ActionType::Delete => {
             if !user.check_scope(&Scope::Delete) {
                 return Err(MicropubError {
-                    error: ErrorType::InvalidScope,
+                    error: ErrorKind::InvalidScope,
                     error_description: "You need a \"delete\" scope for this.".to_owned(),
                 });
             }
@@ -433,7 +423,7 @@ async fn post_action<D: Storage, A: AuthBackend>(
         ActionType::Update => {
             if !user.check_scope(&Scope::Update) {
                 return Err(MicropubError {
-                    error: ErrorType::InvalidScope,
+                    error: ErrorKind::InvalidScope,
                     error_description: "You need an \"update\" scope for this.".to_owned(),
                 });
             }
@@ -441,7 +431,7 @@ async fn post_action<D: Storage, A: AuthBackend>(
             db.update_post(
                 &action.url,
                 action.update.ok_or(MicropubError {
-                    error: ErrorType::InvalidRequest,
+                    error: ErrorKind::InvalidRequest,
                     error_description: "Update request is not set.".to_owned(),
                 })?
             )
@@ -483,7 +473,7 @@ async fn dispatch_body(
             // quick sanity check
             if !body.is_object() || !body["type"].is_array() {
                 return Err(MicropubError {
-                    error: ErrorType::InvalidRequest,
+                    error: ErrorKind::InvalidRequest,
                     error_description: "Invalid MF2-JSON detected: `.` should be an object, `.type` should be an array of MF2 types".to_owned()
                 });
             }
@@ -491,7 +481,7 @@ async fn dispatch_body(
             Ok(PostBody::MF2(body))
         } else {
             Err(MicropubError {
-                error: ErrorType::InvalidRequest,
+                error: ErrorKind::InvalidRequest,
                 error_description: "Invalid JSON object passed.".to_owned(),
             })
         }
@@ -502,14 +492,14 @@ async fn dispatch_body(
             Ok(PostBody::MF2(form_to_mf2_json(body)))
         } else {
             Err(MicropubError {
-                error: ErrorType::InvalidRequest,
+                error: ErrorKind::InvalidRequest,
                 error_description: "Invalid form-encoded data. Try h=entry&content=Hello!"
                     .to_owned(),
             })
         }
     } else {
         Err(MicropubError::new(
-            ErrorType::UnsupportedMediaType,
+            ErrorKind::UnsupportedMediaType,
             "This Content-Type is not recognized. Try application/json instead?",
         ))
     }
@@ -553,7 +543,7 @@ pub(crate) async fn query<D: Storage, A: AuthBackend>(
         query
     } else {
         return MicropubError::new(
-            ErrorType::InvalidRequest,
+            ErrorKind::InvalidRequest,
             "Invalid query provided. Try ?q=config to see what you can do."
         ).into_response();
     };
@@ -565,7 +555,7 @@ pub(crate) async fn query<D: Storage, A: AuthBackend>(
         != &host
     {
         return MicropubError::new(
-            ErrorType::NotAuthorized,
+            ErrorKind::NotAuthorized,
             "This website doesn't belong to you.",
         )
             .into_response();
@@ -585,26 +575,31 @@ pub(crate) async fn query<D: Storage, A: AuthBackend>(
                 Ok(chans) => chans,
                 Err(err) => {
                     return MicropubError::new(
-                        ErrorType::InternalServerError,
+                        ErrorKind::InternalServerError,
                         &format!("Error fetching channels: {}", err),
                     )
                     .into_response()
                 }
             };
 
-            axum::response::Json(json!({
-                "q": [
+            axum::response::Json(kittybox_util::micropub::Config {
+                q: vec![
                     QueryType::Source,
                     QueryType::Config,
                     QueryType::Channel,
                     QueryType::SyndicateTo,
                     QueryType::Category
                 ],
-                "channels": channels,
-                "_kittybox_authority": user.me.as_str(),
-                "syndicate-to": [],
-                "media-endpoint": user.me.join("/.kittybox/media").unwrap().as_str()
-            }))
+                channels: Some(channels),
+                syndicate_to: None,
+                media_endpoint: Some(user.me.join("/.kittybox/media").unwrap()),
+                other: {
+                    let mut map = std::collections::HashMap::new();
+                    map.insert("kittybox_authority".to_string(), serde_json::Value::String(user.me.to_string()));
+
+                    map
+                }
+            })
             .into_response()
         }
         QueryType::Source => {
@@ -614,13 +609,13 @@ pub(crate) async fn query<D: Storage, A: AuthBackend>(
                         Ok(some) => match some {
                             Some(post) => axum::response::Json(&post).into_response(),
                             None => MicropubError::new(
-                                ErrorType::NotFound,
+                                ErrorKind::NotFound,
                                 "The specified MF2 object was not found in database.",
                             )
                             .into_response(),
                         },
                         Err(err) => MicropubError::new(
-                            ErrorType::InternalServerError,
+                            ErrorKind::InternalServerError,
                             &format!("Backend error: {}", err),
                         )
                         .into_response(),
@@ -631,7 +626,7 @@ pub(crate) async fn query<D: Storage, A: AuthBackend>(
                     // 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,
+                        ErrorKind::InvalidRequest,
                         "Querying for post list is not implemented yet.",
                     )
                     .into_response()
@@ -641,7 +636,7 @@ pub(crate) async fn query<D: Storage, A: AuthBackend>(
         QueryType::Channel => match db.get_channels(&user.me).await {
             Ok(chans) => axum::response::Json(json!({ "channels": chans })).into_response(),
             Err(err) => MicropubError::new(
-                ErrorType::InternalServerError,
+                ErrorKind::InternalServerError,
                 &format!("Error fetching channels: {}", err),
             )
             .into_response(),
@@ -654,13 +649,17 @@ pub(crate) async fn query<D: Storage, A: AuthBackend>(
                 Ok(categories) => categories,
                 Err(err) => {
                     return MicropubError::new(
-                        ErrorType::InternalServerError,
+                        ErrorKind::InternalServerError,
                         &format!("Error fetching categories: {}", err)
                     ).into_response()
                 }
             };
             axum::response::Json(json!({ "categories": categories })).into_response()
-        }
+        },
+        QueryType::Unknown(q) => return MicropubError {
+            error: ErrorKind::InvalidRequest,
+            error_description: format!("Invalid query: {}", q)
+        }.into_response(),
     }
 }
 
@@ -776,7 +775,7 @@ mod tests {
             .await
             .unwrap_err();
 
-        assert_eq!(err.error, super::ErrorType::InvalidScope);
+        assert_eq!(err.error, super::ErrorKind::InvalidScope);
 
         let hashmap = db.mapping.read().await;
         assert!(hashmap.is_empty());
@@ -806,7 +805,7 @@ mod tests {
             .await
             .unwrap_err();
 
-        assert_eq!(err.error, super::ErrorType::Forbidden);
+        assert_eq!(err.error, super::ErrorKind::Forbidden);
 
         let hashmap = db.mapping.read().await;
         assert!(hashmap.is_empty());
@@ -873,6 +872,6 @@ mod tests {
             .by_ref()
             .fold(Vec::new(), |mut a, i| { a.extend(i); a});
         let json: MicropubError = serde_json::from_slice(&body as &[u8]).unwrap();
-        assert_eq!(json.error, super::ErrorType::NotAuthorized);
+        assert_eq!(json.error, super::ErrorKind::NotAuthorized);
     }
 }