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