]> Untitled Git - lemmy.git/blob - src/routes/webfinger.rs
Move websocket code into workspace (#107)
[lemmy.git] / src / routes / webfinger.rs
1 use actix_web::{error::ErrorBadRequest, web::Query, *};
2 use anyhow::anyhow;
3 use lemmy_db::{community::Community, user::User_};
4 use lemmy_structs::{blocking, WebFingerLink, WebFingerResponse};
5 use lemmy_utils::{
6   settings::Settings,
7   LemmyError,
8   WEBFINGER_COMMUNITY_REGEX,
9   WEBFINGER_USER_REGEX,
10 };
11 use lemmy_websocket::LemmyContext;
12 use serde::Deserialize;
13
14 #[derive(Deserialize)]
15 pub struct Params {
16   resource: String,
17 }
18
19 pub fn config(cfg: &mut web::ServiceConfig) {
20   if Settings::get().federation.enabled {
21     cfg.route(
22       ".well-known/webfinger",
23       web::get().to(get_webfinger_response),
24     );
25   }
26 }
27
28 /// Responds to webfinger requests of the following format. There isn't any real documentation for
29 /// this, but it described in this blog post:
30 /// https://mastodon.social/.well-known/webfinger?resource=acct:gargron@mastodon.social
31 ///
32 /// You can also view the webfinger response that Mastodon sends:
33 /// https://radical.town/.well-known/webfinger?resource=acct:felix@radical.town
34 async fn get_webfinger_response(
35   info: Query<Params>,
36   context: web::Data<LemmyContext>,
37 ) -> Result<HttpResponse, Error> {
38   let community_regex_parsed = WEBFINGER_COMMUNITY_REGEX
39     .captures(&info.resource)
40     .map(|c| c.get(1))
41     .flatten();
42
43   let user_regex_parsed = WEBFINGER_USER_REGEX
44     .captures(&info.resource)
45     .map(|c| c.get(1))
46     .flatten();
47
48   let url = if let Some(community_name) = community_regex_parsed {
49     let community_name = community_name.as_str().to_owned();
50     // Make sure the requested community exists.
51     blocking(context.pool(), move |conn| {
52       Community::read_from_name(conn, &community_name)
53     })
54     .await?
55     .map_err(|_| ErrorBadRequest(LemmyError::from(anyhow!("not_found"))))?
56     .actor_id
57   } else if let Some(user_name) = user_regex_parsed {
58     let user_name = user_name.as_str().to_owned();
59     // Make sure the requested user exists.
60     blocking(context.pool(), move |conn| {
61       User_::read_from_name(conn, &user_name)
62     })
63     .await?
64     .map_err(|_| ErrorBadRequest(LemmyError::from(anyhow!("not_found"))))?
65     .actor_id
66   } else {
67     return Err(ErrorBadRequest(LemmyError::from(anyhow!("not_found"))));
68   };
69
70   let json = WebFingerResponse {
71     subject: info.resource.to_owned(),
72     aliases: vec![url.to_owned()],
73     links: vec![
74       WebFingerLink {
75         rel: Some("http://webfinger.net/rel/profile-page".to_string()),
76         type_: Some("text/html".to_string()),
77         href: Some(url.to_owned()),
78         template: None,
79       },
80       WebFingerLink {
81         rel: Some("self".to_string()),
82         type_: Some("application/activity+json".to_string()),
83         href: Some(url),
84         template: None,
85       }, // TODO: this also needs to return the subscribe link once that's implemented
86          //{
87          //  "rel": "http://ostatus.org/schema/1.0/subscribe",
88          //  "template": "https://my_instance.com/authorize_interaction?uri={uri}"
89          //}
90     ],
91   };
92
93   Ok(HttpResponse::Ok().json(json))
94 }