]> Untitled Git - lemmy.git/blob - src/api_routes_http.rs
cb735f807c73903b1b72ecdc5e2ed0fee52ef9f1
[lemmy.git] / src / api_routes_http.rs
1 use actix_web::{guard, web, Error, HttpResponse, Result};
2 use lemmy_api::Perform;
3 use lemmy_api_common::{
4   comment::{
5     CreateComment,
6     CreateCommentLike,
7     CreateCommentReport,
8     DeleteComment,
9     DistinguishComment,
10     EditComment,
11     GetComment,
12     ListCommentReports,
13     RemoveComment,
14     ResolveCommentReport,
15     SaveComment,
16   },
17   community::{
18     AddModToCommunity,
19     BanFromCommunity,
20     BlockCommunity,
21     CreateCommunity,
22     DeleteCommunity,
23     EditCommunity,
24     FollowCommunity,
25     HideCommunity,
26     ListCommunities,
27     RemoveCommunity,
28     TransferCommunity,
29   },
30   context::LemmyContext,
31   custom_emoji::{CreateCustomEmoji, DeleteCustomEmoji, EditCustomEmoji},
32   person::{
33     AddAdmin,
34     BanPerson,
35     BlockPerson,
36     ChangePassword,
37     DeleteAccount,
38     GetBannedPersons,
39     GetCaptcha,
40     GetPersonMentions,
41     GetReplies,
42     GetReportCount,
43     GetUnreadCount,
44     Login,
45     MarkAllAsRead,
46     MarkCommentReplyAsRead,
47     MarkPersonMentionAsRead,
48     PasswordChangeAfterReset,
49     PasswordReset,
50     Register,
51     SaveUserSettings,
52     VerifyEmail,
53   },
54   post::{
55     CreatePost,
56     CreatePostLike,
57     CreatePostReport,
58     DeletePost,
59     EditPost,
60     FeaturePost,
61     GetPost,
62     GetSiteMetadata,
63     ListPostReports,
64     LockPost,
65     MarkPostAsRead,
66     RemovePost,
67     ResolvePostReport,
68     SavePost,
69   },
70   private_message::{
71     CreatePrivateMessage,
72     CreatePrivateMessageReport,
73     DeletePrivateMessage,
74     EditPrivateMessage,
75     GetPrivateMessages,
76     ListPrivateMessageReports,
77     MarkPrivateMessageAsRead,
78     ResolvePrivateMessageReport,
79   },
80   site::{
81     ApproveRegistrationApplication,
82     CreateSite,
83     EditSite,
84     GetFederatedInstances,
85     GetModlog,
86     GetSite,
87     GetUnreadRegistrationApplicationCount,
88     LeaveAdmin,
89     ListRegistrationApplications,
90     PurgeComment,
91     PurgeCommunity,
92     PurgePerson,
93     PurgePost,
94   },
95 };
96 use lemmy_api_crud::PerformCrud;
97 use lemmy_apub::{
98   api::{
99     list_comments::list_comments,
100     list_posts::list_posts,
101     read_community::read_community,
102     read_person::read_person,
103     resolve_object::resolve_object,
104     search::search,
105   },
106   SendActivity,
107 };
108 use lemmy_utils::{rate_limit::RateLimitCell, spawn_try_task, SYNCHRONOUS_FEDERATION};
109 use serde::Deserialize;
110
111 pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
112   cfg.service(
113     web::scope("/api/v3")
114       // Site
115       .service(
116         web::scope("/site")
117           .wrap(rate_limit.message())
118           .route("", web::get().to(route_get_crud::<GetSite>))
119           // Admin Actions
120           .route("", web::post().to(route_post_crud::<CreateSite>))
121           .route("", web::put().to(route_post_crud::<EditSite>)),
122       )
123       .service(
124         web::resource("/modlog")
125           .wrap(rate_limit.message())
126           .route(web::get().to(route_get::<GetModlog>)),
127       )
128       .service(
129         web::resource("/search")
130           .wrap(rate_limit.search())
131           .route(web::get().to(search)),
132       )
133       .service(
134         web::resource("/resolve_object")
135           .wrap(rate_limit.message())
136           .route(web::get().to(resolve_object)),
137       )
138       // Community
139       .service(
140         web::resource("/community")
141           .guard(guard::Post())
142           .wrap(rate_limit.register())
143           .route(web::post().to(route_post_crud::<CreateCommunity>)),
144       )
145       .service(
146         web::scope("/community")
147           .wrap(rate_limit.message())
148           .route("", web::get().to(read_community))
149           .route("", web::put().to(route_post_crud::<EditCommunity>))
150           .route("/hide", web::put().to(route_post::<HideCommunity>))
151           .route("/list", web::get().to(route_get_crud::<ListCommunities>))
152           .route("/follow", web::post().to(route_post::<FollowCommunity>))
153           .route("/block", web::post().to(route_post::<BlockCommunity>))
154           .route(
155             "/delete",
156             web::post().to(route_post_crud::<DeleteCommunity>),
157           )
158           // Mod Actions
159           .route(
160             "/remove",
161             web::post().to(route_post_crud::<RemoveCommunity>),
162           )
163           .route("/transfer", web::post().to(route_post::<TransferCommunity>))
164           .route("/ban_user", web::post().to(route_post::<BanFromCommunity>))
165           .route("/mod", web::post().to(route_post::<AddModToCommunity>)),
166       )
167       .service(
168         web::scope("/federated_instances")
169           .wrap(rate_limit.message())
170           .route("", web::get().to(route_get::<GetFederatedInstances>)),
171       )
172       // Post
173       .service(
174         // Handle POST to /post separately to add the post() rate limitter
175         web::resource("/post")
176           .guard(guard::Post())
177           .wrap(rate_limit.post())
178           .route(web::post().to(route_post_crud::<CreatePost>)),
179       )
180       .service(
181         web::scope("/post")
182           .wrap(rate_limit.message())
183           .route("", web::get().to(route_get_crud::<GetPost>))
184           .route("", web::put().to(route_post_crud::<EditPost>))
185           .route("/delete", web::post().to(route_post_crud::<DeletePost>))
186           .route("/remove", web::post().to(route_post_crud::<RemovePost>))
187           .route(
188             "/mark_as_read",
189             web::post().to(route_post::<MarkPostAsRead>),
190           )
191           .route("/lock", web::post().to(route_post::<LockPost>))
192           .route("/feature", web::post().to(route_post::<FeaturePost>))
193           .route("/list", web::get().to(list_posts))
194           .route("/like", web::post().to(route_post::<CreatePostLike>))
195           .route("/save", web::put().to(route_post::<SavePost>))
196           .route("/report", web::post().to(route_post::<CreatePostReport>))
197           .route(
198             "/report/resolve",
199             web::put().to(route_post::<ResolvePostReport>),
200           )
201           .route("/report/list", web::get().to(route_get::<ListPostReports>))
202           .route(
203             "/site_metadata",
204             web::get().to(route_get::<GetSiteMetadata>),
205           ),
206       )
207       // Comment
208       .service(
209         // Handle POST to /comment separately to add the comment() rate limitter
210         web::resource("/comment")
211           .guard(guard::Post())
212           .wrap(rate_limit.comment())
213           .route(web::post().to(route_post_crud::<CreateComment>)),
214       )
215       .service(
216         web::scope("/comment")
217           .wrap(rate_limit.message())
218           .route("", web::get().to(route_get_crud::<GetComment>))
219           .route("", web::put().to(route_post_crud::<EditComment>))
220           .route("/delete", web::post().to(route_post_crud::<DeleteComment>))
221           .route("/remove", web::post().to(route_post_crud::<RemoveComment>))
222           .route(
223             "/mark_as_read",
224             web::post().to(route_post::<MarkCommentReplyAsRead>),
225           )
226           .route(
227             "/distinguish",
228             web::post().to(route_post::<DistinguishComment>),
229           )
230           .route("/like", web::post().to(route_post::<CreateCommentLike>))
231           .route("/save", web::put().to(route_post::<SaveComment>))
232           .route("/list", web::get().to(list_comments))
233           .route("/report", web::post().to(route_post::<CreateCommentReport>))
234           .route(
235             "/report/resolve",
236             web::put().to(route_post::<ResolveCommentReport>),
237           )
238           .route(
239             "/report/list",
240             web::get().to(route_get::<ListCommentReports>),
241           ),
242       )
243       // Private Message
244       .service(
245         web::scope("/private_message")
246           .wrap(rate_limit.message())
247           .route("/list", web::get().to(route_get_crud::<GetPrivateMessages>))
248           .route("", web::post().to(route_post_crud::<CreatePrivateMessage>))
249           .route("", web::put().to(route_post_crud::<EditPrivateMessage>))
250           .route(
251             "/delete",
252             web::post().to(route_post_crud::<DeletePrivateMessage>),
253           )
254           .route(
255             "/mark_as_read",
256             web::post().to(route_post::<MarkPrivateMessageAsRead>),
257           )
258           .route(
259             "/report",
260             web::post().to(route_post::<CreatePrivateMessageReport>),
261           )
262           .route(
263             "/report/resolve",
264             web::put().to(route_post::<ResolvePrivateMessageReport>),
265           )
266           .route(
267             "/report/list",
268             web::get().to(route_get::<ListPrivateMessageReports>),
269           ),
270       )
271       // User
272       .service(
273         // Account action, I don't like that it's in /user maybe /accounts
274         // Handle /user/register separately to add the register() rate limitter
275         web::resource("/user/register")
276           .guard(guard::Post())
277           .wrap(rate_limit.register())
278           .route(web::post().to(route_post_crud::<Register>)),
279       )
280       .service(
281         // Handle captcha separately
282         web::resource("/user/get_captcha")
283           .wrap(rate_limit.post())
284           .route(web::get().to(route_get::<GetCaptcha>)),
285       )
286       // User actions
287       .service(
288         web::scope("/user")
289           .wrap(rate_limit.message())
290           .route("", web::get().to(read_person))
291           .route("/mention", web::get().to(route_get::<GetPersonMentions>))
292           .route(
293             "/mention/mark_as_read",
294             web::post().to(route_post::<MarkPersonMentionAsRead>),
295           )
296           .route("/replies", web::get().to(route_get::<GetReplies>))
297           // Admin action. I don't like that it's in /user
298           .route("/ban", web::post().to(route_post::<BanPerson>))
299           .route("/banned", web::get().to(route_get::<GetBannedPersons>))
300           .route("/block", web::post().to(route_post::<BlockPerson>))
301           // Account actions. I don't like that they're in /user maybe /accounts
302           .route("/login", web::post().to(route_post::<Login>))
303           .route(
304             "/delete_account",
305             web::post().to(route_post_crud::<DeleteAccount>),
306           )
307           .route(
308             "/password_reset",
309             web::post().to(route_post::<PasswordReset>),
310           )
311           .route(
312             "/password_change",
313             web::post().to(route_post::<PasswordChangeAfterReset>),
314           )
315           // mark_all_as_read feels off being in this section as well
316           .route(
317             "/mark_all_as_read",
318             web::post().to(route_post::<MarkAllAsRead>),
319           )
320           .route(
321             "/save_user_settings",
322             web::put().to(route_post::<SaveUserSettings>),
323           )
324           .route(
325             "/change_password",
326             web::put().to(route_post::<ChangePassword>),
327           )
328           .route("/report_count", web::get().to(route_get::<GetReportCount>))
329           .route("/unread_count", web::get().to(route_get::<GetUnreadCount>))
330           .route("/verify_email", web::post().to(route_post::<VerifyEmail>))
331           .route("/leave_admin", web::post().to(route_post::<LeaveAdmin>)),
332       )
333       // Admin Actions
334       .service(
335         web::scope("/admin")
336           .wrap(rate_limit.message())
337           .route("/add", web::post().to(route_post::<AddAdmin>))
338           .route(
339             "/registration_application/count",
340             web::get().to(route_get::<GetUnreadRegistrationApplicationCount>),
341           )
342           .route(
343             "/registration_application/list",
344             web::get().to(route_get::<ListRegistrationApplications>),
345           )
346           .route(
347             "/registration_application/approve",
348             web::put().to(route_post::<ApproveRegistrationApplication>),
349           )
350           .service(
351             web::scope("/purge")
352               .route("/person", web::post().to(route_post::<PurgePerson>))
353               .route("/community", web::post().to(route_post::<PurgeCommunity>))
354               .route("/post", web::post().to(route_post::<PurgePost>))
355               .route("/comment", web::post().to(route_post::<PurgeComment>)),
356           ),
357       )
358       .service(
359         web::scope("/custom_emoji")
360           .wrap(rate_limit.message())
361           .route("", web::post().to(route_post_crud::<CreateCustomEmoji>))
362           .route("", web::put().to(route_post_crud::<EditCustomEmoji>))
363           .route(
364             "/delete",
365             web::post().to(route_post_crud::<DeleteCustomEmoji>),
366           ),
367       ),
368   );
369 }
370
371 async fn perform<'a, Data>(
372   data: Data,
373   context: web::Data<LemmyContext>,
374   apub_data: activitypub_federation::config::Data<LemmyContext>,
375 ) -> Result<HttpResponse, Error>
376 where
377   Data: Perform
378     + SendActivity<Response = <Data as Perform>::Response>
379     + Clone
380     + Deserialize<'a>
381     + Send
382     + 'static,
383 {
384   let res = data.perform(&context).await?;
385   let res_clone = res.clone();
386   let fed_task = async move { SendActivity::send_activity(&data, &res_clone, &apub_data).await };
387   if *SYNCHRONOUS_FEDERATION {
388     fed_task.await?;
389   } else {
390     spawn_try_task(fed_task);
391   }
392   Ok(HttpResponse::Ok().json(&res))
393 }
394
395 async fn route_get<'a, Data>(
396   data: web::Query<Data>,
397   context: web::Data<LemmyContext>,
398   apub_data: activitypub_federation::config::Data<LemmyContext>,
399 ) -> Result<HttpResponse, Error>
400 where
401   Data: Perform
402     + SendActivity<Response = <Data as Perform>::Response>
403     + Clone
404     + Deserialize<'a>
405     + Send
406     + 'static,
407 {
408   perform::<Data>(data.0, context, apub_data).await
409 }
410
411 async fn route_post<'a, Data>(
412   data: web::Json<Data>,
413   context: web::Data<LemmyContext>,
414   apub_data: activitypub_federation::config::Data<LemmyContext>,
415 ) -> Result<HttpResponse, Error>
416 where
417   Data: Perform
418     + SendActivity<Response = <Data as Perform>::Response>
419     + Clone
420     + Deserialize<'a>
421     + Send
422     + 'static,
423 {
424   perform::<Data>(data.0, context, apub_data).await
425 }
426
427 async fn perform_crud<'a, Data>(
428   data: Data,
429   context: web::Data<LemmyContext>,
430   apub_data: activitypub_federation::config::Data<LemmyContext>,
431 ) -> Result<HttpResponse, Error>
432 where
433   Data: PerformCrud
434     + SendActivity<Response = <Data as PerformCrud>::Response>
435     + Clone
436     + Deserialize<'a>
437     + Send
438     + 'static,
439 {
440   let res = data.perform(&context).await?;
441   let res_clone = res.clone();
442   let fed_task = async move { SendActivity::send_activity(&data, &res_clone, &apub_data).await };
443   if *SYNCHRONOUS_FEDERATION {
444     fed_task.await?;
445   } else {
446     spawn_try_task(fed_task);
447   }
448   Ok(HttpResponse::Ok().json(&res))
449 }
450
451 async fn route_get_crud<'a, Data>(
452   data: web::Query<Data>,
453   context: web::Data<LemmyContext>,
454   apub_data: activitypub_federation::config::Data<LemmyContext>,
455 ) -> Result<HttpResponse, Error>
456 where
457   Data: PerformCrud
458     + SendActivity<Response = <Data as PerformCrud>::Response>
459     + Clone
460     + Deserialize<'a>
461     + Send
462     + 'static,
463 {
464   perform_crud::<Data>(data.0, context, apub_data).await
465 }
466
467 async fn route_post_crud<'a, Data>(
468   data: web::Json<Data>,
469   context: web::Data<LemmyContext>,
470   apub_data: activitypub_federation::config::Data<LemmyContext>,
471 ) -> Result<HttpResponse, Error>
472 where
473   Data: PerformCrud
474     + SendActivity<Response = <Data as PerformCrud>::Response>
475     + Clone
476     + Deserialize<'a>
477     + Send
478     + 'static,
479 {
480   perform_crud::<Data>(data.0, context, apub_data).await
481 }