diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/frontend/mod.rs | 4 | ||||
-rw-r--r-- | src/frontend/templates/onboarding.rs | 2 | ||||
-rw-r--r-- | src/lib.rs | 411 | ||||
-rw-r--r-- | src/micropub/mod.rs | 6 |
4 files changed, 6 insertions, 417 deletions
diff --git a/src/frontend/mod.rs b/src/frontend/mod.rs index 49faf59..b594fc6 100644 --- a/src/frontend/mod.rs +++ b/src/frontend/mod.rs @@ -290,9 +290,7 @@ pub fn homepage<D: Storage>(db: D, endpoints: IndiewebEndpoints) -> impl Filter< hyper::Uri::from_static("/onboarding") )) as Box<dyn warp::Reply> } - _ => { - todo!("Handle cases where either main h-card or main h-feed are deleted") - } + _ => unreachable!() } }) } diff --git a/src/frontend/templates/onboarding.rs b/src/frontend/templates/onboarding.rs index f95e1e6..9d0f2e1 100644 --- a/src/frontend/templates/onboarding.rs +++ b/src/frontend/templates/onboarding.rs @@ -21,7 +21,7 @@ markup::define! { p { "Sadly, it's very hard or even impossible to recreate this without any JavaScript. " "Good news though - the code is " b { "open-source AND free software" } - " (under MIT (X11) or Apache-2.0 license - your choice) " + " (under GNU AGPLv3) " "and I promise to not obfuscate it or minify it. " a[href="/static/onboarding.js"] { "Here" } "'s the link - you can try reading it so you'll be 200% sure " diff --git a/src/lib.rs b/src/lib.rs index ffef443..2709022 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![deny(unsafe_code)] +#![forbid(unsafe_code)] #![warn(clippy::todo)] pub mod metrics; @@ -109,412 +109,3 @@ pub mod util { } } } -// fn equip_app<Storage>(mut app: App<Storage>) -> App<Storage> -// where -// Storage: database::Storage + Send + Sync + Clone, -// { -// app.at("/micropub") -// .with(CORSMiddleware {}) -// .with(IndieAuthMiddleware::new()) -// .get(micropub::get_handler) -// .post(micropub::post_handler); -// // The Micropub client. It'll start small, but could grow into something full-featured -// app.at("/micropub/client").get(|_: Request<_>| async move { -// Ok(Response::builder(200) -// .body(MICROPUB_CLIENT) -// .content_type("text/html") -// .build()) -// }); -// app.at("/") -// .with(CORSMiddleware {}) -// .with(frontend::ErrorHandlerMiddleware {}) -// .get(frontend::mainpage) -// .post(frontend::onboarding_receiver); -// app.at("/login") -// .with(frontend::ErrorHandlerMiddleware {}) -// .get(frontend::login::form) -// .post(frontend::login::handler); -// app.at("/login/callback") -// .with(frontend::ErrorHandlerMiddleware {}) -// .get(frontend::login::callback); -// app.at("/static/*path") -// .with(frontend::ErrorHandlerMiddleware {}) -// .get(frontend::handle_static); -// app.at("/*path") -// .with(frontend::ErrorHandlerMiddleware {}) -// .get(frontend::render_post); -// app.at("/coffee") -// .with(frontend::ErrorHandlerMiddleware {}) -// .get(frontend::coffee); -// // TODO make sure the health check actually checks the backend or something -// // otherwise it'll get false-negatives for application faults like resource -// // exhaustion -// app.at("/health").get(|_| async { Ok("OK") }); -// app.at("/metrics").get(metrics::gather); - -// app.with(metrics::InstrumentationMiddleware {}); -// app.with( -// tide::sessions::SessionMiddleware::new( -// tide::sessions::CookieStore::new(), -// app.state().cookie_secret.as_bytes(), -// ) -// .with_cookie_name("kittybox_session") -// .without_save_unchanged(), -// ); -// app -// } - -/*#[cfg(feature="redis")] -pub async fn get_app_with_redis( - token_endpoint: surf::Url, - authorization_endpoint: surf::Url, - redis_uri: String, - media_endpoint: Option<String>, - internal_token: Option<String>, -) -> App<database::RedisStorage> { - let app = tide::with_state(ApplicationState { - token_endpoint, - media_endpoint, - authorization_endpoint, - internal_token, - storage: database::RedisStorage::new(redis_uri).await.unwrap(), - http_client: surf::Client::new(), - }); - - equip_app(app) -}*/ - -/*#[cfg(test)] -pub async fn get_app_with_test_file( - token_endpoint: surf::Url, -) -> ( - tempdir::TempDir, - database::FileStorage, - App<database::FileStorage>, -) { - use surf::Url; - let tempdir = tempdir::TempDir::new("file").expect("Failed to create tempdir"); - let backend = database::FileStorage::new(tempdir.path().to_path_buf()) - .await - .unwrap(); - let app = tide::with_state(ApplicationState { - token_endpoint, - media_endpoint: None, - authorization_endpoint: Url::parse("https://indieauth.com/auth").unwrap(), - storage: backend.clone(), - internal_token: None, - cookie_secret: "1234567890abcdefghijklmnopqrstuvwxyz".to_string(), - http_client: surf::Client::new(), - }); - (tempdir, backend, equip_app(app)) -} - -#[cfg(all(redis, test))] -pub async fn get_app_with_test_redis( - token_endpoint: surf::Url, -) -> ( - database::RedisInstance, - database::RedisStorage, - App<database::RedisStorage>, -) { - use surf::Url; - - let redis_instance = database::get_redis_instance().await; - let backend = database::RedisStorage::new(redis_instance.uri().to_string()) - .await - .unwrap(); - let app = tide::with_state(ApplicationState { - token_endpoint, - media_endpoint: None, - authorization_endpoint: Url::parse("https://indieauth.com/auth").unwrap(), - storage: backend.clone(), - internal_token: None, - http_client: surf::Client::new(), - }); - (redis_instance, backend, equip_app(app)) -}*/ - -/*#[cfg(test)] -#[allow(unused_variables)] -mod tests { - use super::*; - use database::Storage; - use mockito::mock; - use serde_json::json; - use tide_testing::TideTestingExt; - - // Helpers - async fn create_app() -> ( - database::FileStorage, - App<database::FileStorage>, - tempdir::TempDir, - ) { - //get_app_with_memory_for_testing(surf::Url::parse(&*mockito::server_url()).unwrap()).await - let (r, b, a) = - get_app_with_test_file(surf::Url::parse(&*mockito::server_url()).unwrap()).await; - (b, a, r) - } - - async fn post_json( - app: &App<database::FileStorage>, - json: serde_json::Value, - ) -> surf::Response { - let request = app - .post("/micropub") - .header("Authorization", "Bearer test") - .header("Content-Type", "application/json") - .body(json); - return request.send().await.unwrap(); - } - - #[async_std::test] - async fn test_no_deletion_of_others_posts() { - let _m = mock("GET", "/") - .with_status(200) - .with_header("Content-Type", "application/json") - .with_body(r#"{"me": "https://fireburn.ru", "client_id": "https://quill.p3k.io/", "scope": "create update media"}"#) - .create(); - - let (db, app, _r) = create_app().await; - - let mut response = post_json( - &app, - json!({ - "type": ["h-entry"], - "properties": { - "content": ["This is content!"] - } - }), - ) - .await; - println!( - "{:#}", - response.body_json::<serde_json::Value>().await.unwrap() - ); - assert!(response.status() == 201 || response.status() == 202); - let uid = response.header("Location").unwrap().last().to_string(); - drop(_m); - let _m = mock("GET", "/") - .with_status(200) - .with_header("Content-Type", "application/json") - .with_body(r#"{"me": "https://aaronparecki.com/", "client_id": "https://quill.p3k.io/", "scope": "create update delete media"}"#) - .create(); - - let mut response = app - .post("/micropub") - .header("Authorization", "Bearer awoo") - .header("Content-Type", "application/json") - .body(json!({ "action": "delete", "url": uid })) - .send() - .await - .unwrap(); - println!("{}", response.body_string().await.unwrap()); - assert_eq!(response.status(), 403); - } - - #[async_std::test] - async fn test_no_posting_to_others_websites() { - let _m = mock("GET", "/") - .with_status(200) - .with_header("Content-Type", "application/json") - .with_body(r#"{"me": "https://fireburn.ru", "client_id": "https://quill.p3k.io/", "scope": "create update media"}"#) - .create(); - - let (db, app, _r) = create_app().await; - - let response = post_json( - &app, - json!({ - "type": ["h-entry"], - "properties": { - "content": ["Fake news about Aaron Parecki!"], - "uid": ["https://aaronparecki.com/posts/fake-news"] - } - }), - ) - .await; - assert_eq!(response.status(), 403); - - let response = post_json( - &app, - json!({ - "type": ["h-entry"], - "properties": { - "content": ["More fake news about Aaron Parecki!"], - "url": ["https://aaronparecki.com/posts/more-fake-news"] - } - }), - ) - .await; - // Should be posted successfully, but... - assert!(response.status() == 201 || response.status() == 202); - // ...won't be available on a foreign URL - assert!(db - .get_post("https://aaronparecki.com/posts/more-fake-news") - .await - .unwrap() - .is_none()); - - let response = post_json(&app, json!({ - "type": ["h-entry"], - "properties": { - "content": ["Sneaky advertisement designed to creep into someone else's feed! Buy whatever I'm promoting!"], - "channel": ["https://aaronparecki.com/feeds/main"] - } - })).await; - assert_eq!(response.status(), 403); - } - - #[async_std::test] - async fn test_successful_authorization() { - let _m = mock("GET", "/") - .with_status(200) - .with_header("Content-Type", "application/json") - .with_body(r#"{"me": "https://fireburn.ru", "client_id": "https://quill.p3k.io/", "scope": "create update media"}"#) - .create(); - - let (db, app, _r) = create_app().await; - - let response: serde_json::Value = app - .get("/micropub?q=config") - .header("Authorization", "test") - .recv_json() - .await - .unwrap(); - assert!(!response["q"].as_array().unwrap().is_empty()); - } - - #[async_std::test] - async fn test_unsuccessful_authorization() { - let _m = mock("GET", "/") - .with_status(400) - .with_header("Content-Type", "application/json") - .with_body(r#"{"error":"unauthorized","error_description":"A valid access token is required."}"#) - .create(); - - let (db, app, _r) = create_app().await; - - let response: surf::Response = app - .get("/micropub?q=config") - .header("Authorization", "test") - .send() - .await - .unwrap(); - assert_eq!(response.status(), 401); - } - - #[async_std::test] - async fn test_no_auth_header() { - let (db, app, _r) = create_app().await; - - let request: surf::RequestBuilder = app.get("/micropub?q=config"); - let response: surf::Response = request.send().await.unwrap(); - assert_eq!(response.status(), 401); - } - - #[async_std::test] - async fn test_create_post_form_encoded() { - let _m = mock("GET", "/") - .with_status(200) - .with_header("Content-Type", "application/json") - .with_body(r#"{"me": "https://fireburn.ru", "client_id": "https://quill.p3k.io/", "scope": "create update media"}"#) - .create(); - - let (storage, app, _r) = create_app().await; - - let request: surf::RequestBuilder = app - .post("/micropub") - .header("Authorization", "Bearer test") - .header("Content-Type", "application/x-www-form-urlencoded") - .body("h=entry&content=something%20interesting&category[]=test&category[]=stuff"); - let mut response: surf::Response = request.send().await.unwrap(); - println!( - "{:#}", - response.body_json::<serde_json::Value>().await.unwrap() - ); - assert!(response.status() == 201 || response.status() == 202); - let uid = response.header("Location").unwrap().last().to_string(); - // Assume the post is in the database at this point. - let post = storage.get_post(&uid).await.unwrap().unwrap(); - assert_eq!( - post["properties"]["content"][0]["html"] - .as_str() - .unwrap() - .trim(), - "<p>something interesting</p>" - ); - } - - #[async_std::test] - async fn test_create_post_json() { - let _m = mock("GET", "/") - .with_status(200) - .with_header("Content-Type", "application/json") - .with_body(r#"{"me": "https://fireburn.ru", "client_id": "https://quill.p3k.io/", "scope": "create update media"}"#) - .create(); - - let (storage, app, _r) = create_app().await; - - let mut response = post_json( - &app, - json!({ - "type": ["h-entry"], - "properties": { - "content": ["This is content!"] - } - }), - ) - .await; - println!( - "{:#}", - response.body_json::<serde_json::Value>().await.unwrap() - ); - assert!(response.status() == 201 || response.status() == 202); - let uid = response.header("Location").unwrap().last().to_string(); - // Assume the post is in the database at this point. - let post = storage.get_post(&uid).await.unwrap().unwrap(); - assert_eq!( - post["properties"]["content"][0]["html"] - .as_str() - .unwrap() - .trim(), - "<p>This is content!</p>" - ); - let feed = storage - .get_post("https://fireburn.ru/feeds/main") - .await - .unwrap() - .unwrap(); - assert_eq!(feed["children"].as_array().unwrap().len(), 1); - assert_eq!(feed["children"][0].as_str().unwrap(), uid); - let first_uid = uid; - // Test creation of a second post - let mut response = post_json( - &app, - json!({ - "type": ["h-entry"], - "properties": { - "content": ["#moar content for you!"] - } - }), - ) - .await; - println!( - "{:#}", - response.body_json::<serde_json::Value>().await.unwrap() - ); - assert!(response.status() == 201 || response.status() == 202); - let uid = response.header("Location").unwrap().last().to_string(); - // Assume the post is in the database at this point. - //println!("Keys in database: {:?}", storage.mapping.read().await.keys()); - let new_feed = storage - .get_post("https://fireburn.ru/feeds/main") - .await - .unwrap() - .unwrap(); - println!("{}", new_feed["children"]); - assert_eq!(new_feed["children"].as_array().unwrap().len(), 2); - assert_eq!(new_feed["children"][0].as_str().unwrap(), uid); - assert_eq!(new_feed["children"][1].as_str().unwrap(), first_uid); - } -}*/ diff --git a/src/micropub/mod.rs b/src/micropub/mod.rs index 8f5ca09..fbab582 100644 --- a/src/micropub/mod.rs +++ b/src/micropub/mod.rs @@ -288,10 +288,10 @@ pub(crate) async fn _post<D: Storage, T: hyper::client::connect::Connect + Clone ); // TODO: Post-processing the post (aka second write pass) - // - [-] Download rich reply contexts - // - [-] Syndicate the post if requested, add links to the syndicated copies + // - [ ] Download rich reply contexts + // - [ ] Syndicate the post if requested, add links to the syndicated copies // - [ ] Send WebSub notifications to the hub (if we happen to have one) - // - [x] Send webmentions + // - [ ] Send webmentions #[allow(unused_imports)] tokio::task::spawn(async move { use hyper::{Uri, Response, Body, body::HttpBody}; |