Fix error responses and content types
This commit is contained in:
		
							parent
							
								
									25c6dffc85
								
							
						
					
					
						commit
						cbd1ae6fa4
					
				| 
						 | 
				
			
			@ -14,7 +14,8 @@ ports to use for development and deployment.
 | 
			
		|||
 | 
			
		||||
- [x] Webfinger for Cohost projects
 | 
			
		||||
    - [ ] Handle redirects
 | 
			
		||||
- [ ] RSS feeds for projects
 | 
			
		||||
- [x] RSS feeds for projects
 | 
			
		||||
- [x] Index page explaining what's going on
 | 
			
		||||
- [ ] RSS feeds for tags
 | 
			
		||||
- [ ] Atom Extension pagination support
 | 
			
		||||
- [ ] Read More support
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,13 +0,0 @@
 | 
			
		|||
{
 | 
			
		||||
  "subject": "acct:noracodes@example.com",
 | 
			
		||||
  "aliases": [
 | 
			
		||||
    "acct:noracodes@cohost.org"
 | 
			
		||||
  ],
 | 
			
		||||
  "links": [
 | 
			
		||||
    {
 | 
			
		||||
      "rel": "http://webfinger.net/rel/profile-page",
 | 
			
		||||
      "type": "text/html",
 | 
			
		||||
      "href": "https://cohost.org/noracodes"
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										64
									
								
								src/main.rs
								
								
								
								
							
							
						
						
									
										64
									
								
								src/main.rs
								
								
								
								
							| 
						 | 
				
			
			@ -37,8 +37,26 @@ fn index() -> RawHtml<&'static str> {
 | 
			
		|||
    RawHtml(include_str!("../static/index.html"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Responder)]
 | 
			
		||||
#[response(content_type = "application/rss+xml")]
 | 
			
		||||
struct RssResponse {
 | 
			
		||||
    inner: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Responder)]
 | 
			
		||||
#[response(content_type = "text/plain")]
 | 
			
		||||
enum ErrorResponse {
 | 
			
		||||
    #[response(status = 404)]
 | 
			
		||||
    NotFound(String),
 | 
			
		||||
    #[response(status = 500)]
 | 
			
		||||
    InternalError(String),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[get("/<project>/feed.rss?<page>")]
 | 
			
		||||
async fn syndication_rss_route(project: &str, page: Option<u64>) -> Option<String> {
 | 
			
		||||
async fn syndication_rss_route(
 | 
			
		||||
    project: &str,
 | 
			
		||||
    page: Option<u64>,
 | 
			
		||||
) -> Result<RssResponse, ErrorResponse> {
 | 
			
		||||
    let page = page.unwrap_or(0);
 | 
			
		||||
    let project_url = format!("{}{}", COHOST_ACCOUNT_API_URL, project);
 | 
			
		||||
    let posts_url = cohost_posts_api_url(project, page);
 | 
			
		||||
| 
						 | 
				
			
			@ -49,25 +67,28 @@ async fn syndication_rss_route(project: &str, page: Option<u64>) -> Option<Strin
 | 
			
		|||
            StatusCode::OK => match v.json::<CohostAccount>().await {
 | 
			
		||||
                Ok(a) => a,
 | 
			
		||||
                Err(e) => {
 | 
			
		||||
                    eprintln!("Couldn't deserialize Cohost project '{}': {:?}", project, e);
 | 
			
		||||
                    return None;
 | 
			
		||||
                    let err = format!("Couldn't deserialize Cohost project '{}': {:?}", project, e);
 | 
			
		||||
                    eprintln!("{}", err);
 | 
			
		||||
                    return Err(ErrorResponse::InternalError(err));
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            // TODO NORA: Handle possible redirects
 | 
			
		||||
            s => {
 | 
			
		||||
                eprintln!(
 | 
			
		||||
                let err = format!(
 | 
			
		||||
                    "Didn't receive status code 200 for Cohost project '{}'; got {:?} instead.",
 | 
			
		||||
                    project, s
 | 
			
		||||
                );
 | 
			
		||||
                return None;
 | 
			
		||||
                eprintln!("{}", err);
 | 
			
		||||
                return Err(ErrorResponse::NotFound(err));
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        Err(e) => {
 | 
			
		||||
            eprintln!(
 | 
			
		||||
            let err = format!(
 | 
			
		||||
                "Error making request to Cohost for project '{}': {:?}",
 | 
			
		||||
                project, e
 | 
			
		||||
            );
 | 
			
		||||
            return None;
 | 
			
		||||
            eprintln!("{}", err);
 | 
			
		||||
            return Err(ErrorResponse::InternalError(err));
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -76,33 +97,41 @@ async fn syndication_rss_route(project: &str, page: Option<u64>) -> Option<Strin
 | 
			
		|||
        Ok(v) => match v.status() {
 | 
			
		||||
            StatusCode::OK => match v.json::<CohostPostsPage>().await {
 | 
			
		||||
                Ok(page_data) => {
 | 
			
		||||
                    return Some(
 | 
			
		||||
                        syndication::channel_for_posts_page(project, page, project_data, page_data)
 | 
			
		||||
                            .to_string(),
 | 
			
		||||
                    );
 | 
			
		||||
                    return Ok(RssResponse {
 | 
			
		||||
                        inner: syndication::channel_for_posts_page(
 | 
			
		||||
                            project,
 | 
			
		||||
                            page,
 | 
			
		||||
                            project_data,
 | 
			
		||||
                            page_data,
 | 
			
		||||
                        )
 | 
			
		||||
                        .to_string(),
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
                Err(e) => {
 | 
			
		||||
                    eprintln!(
 | 
			
		||||
                    let err = format!(
 | 
			
		||||
                        "Couldn't deserialize Cohost posts page for '{}': {:?}",
 | 
			
		||||
                        project, e
 | 
			
		||||
                    );
 | 
			
		||||
                    eprintln!("{}", err);
 | 
			
		||||
                    return Err(ErrorResponse::InternalError(err));
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            // TODO NORA: Handle possible redirects
 | 
			
		||||
            s => {
 | 
			
		||||
                eprintln!("Didn't receive status code 200 for posts for Cohost project '{}'; got {:?} instead.", page, s);
 | 
			
		||||
                return None;
 | 
			
		||||
                let err = format!("Didn't receive status code 200 for posts for Cohost project '{}'; got {:?} instead.", page, s);
 | 
			
		||||
                eprintln!("{}", err);
 | 
			
		||||
                return Err(ErrorResponse::NotFound(err));
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        Err(e) => {
 | 
			
		||||
            eprintln!(
 | 
			
		||||
            let err = format!(
 | 
			
		||||
                "Error making request to Cohost for posts for project '{}': {:?}",
 | 
			
		||||
                project, e
 | 
			
		||||
            );
 | 
			
		||||
            return None;
 | 
			
		||||
            eprintln!("{}", err);
 | 
			
		||||
            return Err(ErrorResponse::InternalError(err));
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    None
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[get("/.well-known/webfinger?<params..>")]
 | 
			
		||||
| 
						 | 
				
			
			@ -125,6 +154,7 @@ async fn webfinger_route(params: HashMap<String, String>) -> Option<Json<CohostW
 | 
			
		|||
                            return Some(Json(CohostWebfingerResource::new(
 | 
			
		||||
                                param.0.as_str(),
 | 
			
		||||
                                &ARGS.domain,
 | 
			
		||||
                                &ARGS.base_url,
 | 
			
		||||
                            )));
 | 
			
		||||
                        }
 | 
			
		||||
                        Err(e) => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,11 +16,24 @@ pub struct CohostWebfingerProfileLink {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
impl CohostWebfingerResource {
 | 
			
		||||
    pub fn new<S: AsRef<str>, T: AsRef<str>>(project_id: S, domain: T) -> Self {
 | 
			
		||||
    pub fn new<S: AsRef<str>, T: AsRef<str>>(
 | 
			
		||||
        project_id: S,
 | 
			
		||||
        domain: T,
 | 
			
		||||
        base_url: impl AsRef<str>,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        let mut corobel_link = CohostWebfingerProfileLink::new(project_id.as_ref().clone());
 | 
			
		||||
        corobel_link.href = format!(
 | 
			
		||||
            "https://{}{}{}/feed.rss",
 | 
			
		||||
            domain.as_ref().clone(),
 | 
			
		||||
            base_url.as_ref().clone(),
 | 
			
		||||
            project_id.as_ref().clone()
 | 
			
		||||
        );
 | 
			
		||||
        corobel_link.rel = "feed".into();
 | 
			
		||||
        corobel_link.t_type = "application+rss/xml".into();
 | 
			
		||||
        Self {
 | 
			
		||||
            subject: format!("acct:{}@{}", project_id.as_ref(), domain.as_ref()),
 | 
			
		||||
            aliases: vec![format!("acct:{}@cohost.org", project_id.as_ref())],
 | 
			
		||||
            links: vec![CohostWebfingerProfileLink::new(project_id)],
 | 
			
		||||
            links: vec![CohostWebfingerProfileLink::new(project_id), corobel_link],
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -34,14 +47,3 @@ impl CohostWebfingerProfileLink {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn serialize_webfinger_resource() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    let expected_json = include_str!("../samples/corobel/.well-known/webfinger");
 | 
			
		||||
    let resource: CohostWebfingerResource =
 | 
			
		||||
        CohostWebfingerResource::new("noracodes", "example.com");
 | 
			
		||||
    let actual_json = serde_json::to_string_pretty(&resource)?;
 | 
			
		||||
    println!("{}", actual_json);
 | 
			
		||||
    assert_eq!(&expected_json, &actual_json);
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,6 +30,9 @@
 | 
			
		|||
        Go to <code>/project_name/feed.rss</code> to get a feed for a project.
 | 
			
		||||
        For example, <a href="/noracodes/feed.rss"><code>/noracodes/feed.rss</code></a> will give you the feed for my page.
 | 
			
		||||
    </p>
 | 
			
		||||
    <p>
 | 
			
		||||
        Webfinger resources for accounts are provided at the Webfinger well-known URL <code>/.well-known/webfinger?project_name</code>.
 | 
			
		||||
    </p>
 | 
			
		||||
    <p>
 | 
			
		||||
        Brought to you by Leonora Tindall, written in Rust with Rocket.
 | 
			
		||||
    </p>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue