]> Untitled Git - lemmy.git/blob - server/src/api/site.rs
Initial post-listing community non-local.
[lemmy.git] / server / src / api / site.rs
1 use super::*;
2 use crate::api::user::Register;
3 use crate::api::{Oper, Perform};
4 use crate::settings::Settings;
5 use diesel::PgConnection;
6 use log::info;
7 use std::str::FromStr;
8
9 #[derive(Serialize, Deserialize)]
10 pub struct ListCategories {}
11
12 #[derive(Serialize, Deserialize)]
13 pub struct ListCategoriesResponse {
14   categories: Vec<Category>,
15 }
16
17 #[derive(Serialize, Deserialize)]
18 pub struct Search {
19   q: String,
20   type_: String,
21   community_id: Option<i32>,
22   sort: String,
23   page: Option<i64>,
24   limit: Option<i64>,
25   auth: Option<String>,
26 }
27
28 #[derive(Serialize, Deserialize)]
29 pub struct SearchResponse {
30   type_: String,
31   comments: Vec<CommentView>,
32   posts: Vec<PostView>,
33   communities: Vec<CommunityView>,
34   users: Vec<UserView>,
35 }
36
37 #[derive(Serialize, Deserialize)]
38 pub struct GetModlog {
39   mod_user_id: Option<i32>,
40   community_id: Option<i32>,
41   page: Option<i64>,
42   limit: Option<i64>,
43 }
44
45 #[derive(Serialize, Deserialize)]
46 pub struct GetModlogResponse {
47   removed_posts: Vec<ModRemovePostView>,
48   locked_posts: Vec<ModLockPostView>,
49   stickied_posts: Vec<ModStickyPostView>,
50   removed_comments: Vec<ModRemoveCommentView>,
51   removed_communities: Vec<ModRemoveCommunityView>,
52   banned_from_community: Vec<ModBanFromCommunityView>,
53   banned: Vec<ModBanView>,
54   added_to_community: Vec<ModAddCommunityView>,
55   added: Vec<ModAddView>,
56 }
57
58 #[derive(Serialize, Deserialize)]
59 pub struct CreateSite {
60   pub name: String,
61   pub description: Option<String>,
62   pub enable_downvotes: bool,
63   pub open_registration: bool,
64   pub enable_nsfw: bool,
65   pub auth: String,
66 }
67
68 #[derive(Serialize, Deserialize)]
69 pub struct EditSite {
70   name: String,
71   description: Option<String>,
72   enable_downvotes: bool,
73   open_registration: bool,
74   enable_nsfw: bool,
75   auth: String,
76 }
77
78 #[derive(Serialize, Deserialize)]
79 pub struct GetSite {}
80
81 #[derive(Serialize, Deserialize)]
82 pub struct SiteResponse {
83   site: SiteView,
84 }
85
86 #[derive(Serialize, Deserialize)]
87 pub struct GetSiteResponse {
88   site: Option<SiteView>,
89   admins: Vec<UserView>,
90   banned: Vec<UserView>,
91   pub online: usize,
92 }
93
94 #[derive(Serialize, Deserialize)]
95 pub struct TransferSite {
96   user_id: i32,
97   auth: String,
98 }
99
100 impl Perform<ListCategoriesResponse> for Oper<ListCategories> {
101   fn perform(&self, conn: &PgConnection) -> Result<ListCategoriesResponse, Error> {
102     let _data: &ListCategories = &self.data;
103
104     let categories: Vec<Category> = Category::list_all(&conn)?;
105
106     // Return the jwt
107     Ok(ListCategoriesResponse { categories })
108   }
109 }
110
111 impl Perform<GetModlogResponse> for Oper<GetModlog> {
112   fn perform(&self, conn: &PgConnection) -> Result<GetModlogResponse, Error> {
113     let data: &GetModlog = &self.data;
114
115     let removed_posts = ModRemovePostView::list(
116       &conn,
117       data.community_id,
118       data.mod_user_id,
119       data.page,
120       data.limit,
121     )?;
122     let locked_posts = ModLockPostView::list(
123       &conn,
124       data.community_id,
125       data.mod_user_id,
126       data.page,
127       data.limit,
128     )?;
129     let stickied_posts = ModStickyPostView::list(
130       &conn,
131       data.community_id,
132       data.mod_user_id,
133       data.page,
134       data.limit,
135     )?;
136     let removed_comments = ModRemoveCommentView::list(
137       &conn,
138       data.community_id,
139       data.mod_user_id,
140       data.page,
141       data.limit,
142     )?;
143     let banned_from_community = ModBanFromCommunityView::list(
144       &conn,
145       data.community_id,
146       data.mod_user_id,
147       data.page,
148       data.limit,
149     )?;
150     let added_to_community = ModAddCommunityView::list(
151       &conn,
152       data.community_id,
153       data.mod_user_id,
154       data.page,
155       data.limit,
156     )?;
157
158     // These arrays are only for the full modlog, when a community isn't given
159     let (removed_communities, banned, added) = if data.community_id.is_none() {
160       (
161         ModRemoveCommunityView::list(&conn, data.mod_user_id, data.page, data.limit)?,
162         ModBanView::list(&conn, data.mod_user_id, data.page, data.limit)?,
163         ModAddView::list(&conn, data.mod_user_id, data.page, data.limit)?,
164       )
165     } else {
166       (Vec::new(), Vec::new(), Vec::new())
167     };
168
169     // Return the jwt
170     Ok(GetModlogResponse {
171       removed_posts,
172       locked_posts,
173       stickied_posts,
174       removed_comments,
175       removed_communities,
176       banned_from_community,
177       banned,
178       added_to_community,
179       added,
180     })
181   }
182 }
183
184 impl Perform<SiteResponse> for Oper<CreateSite> {
185   fn perform(&self, conn: &PgConnection) -> Result<SiteResponse, Error> {
186     let data: &CreateSite = &self.data;
187
188     let claims = match Claims::decode(&data.auth) {
189       Ok(claims) => claims.claims,
190       Err(_e) => return Err(APIError::err("not_logged_in").into()),
191     };
192
193     if let Err(slurs) = slur_check(&data.name) {
194       return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
195     }
196
197     if let Some(description) = &data.description {
198       if let Err(slurs) = slur_check(description) {
199         return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
200       }
201     }
202
203     let user_id = claims.id;
204
205     // Make sure user is an admin
206     if !UserView::read(&conn, user_id)?.admin {
207       return Err(APIError::err("not_an_admin").into());
208     }
209
210     let site_form = SiteForm {
211       name: data.name.to_owned(),
212       description: data.description.to_owned(),
213       creator_id: user_id,
214       enable_downvotes: data.enable_downvotes,
215       open_registration: data.open_registration,
216       enable_nsfw: data.enable_nsfw,
217       updated: None,
218     };
219
220     match Site::create(&conn, &site_form) {
221       Ok(site) => site,
222       Err(_e) => return Err(APIError::err("site_already_exists").into()),
223     };
224
225     let site_view = SiteView::read(&conn)?;
226
227     Ok(SiteResponse { site: site_view })
228   }
229 }
230
231 impl Perform<SiteResponse> for Oper<EditSite> {
232   fn perform(&self, conn: &PgConnection) -> Result<SiteResponse, Error> {
233     let data: &EditSite = &self.data;
234
235     let claims = match Claims::decode(&data.auth) {
236       Ok(claims) => claims.claims,
237       Err(_e) => return Err(APIError::err("not_logged_in").into()),
238     };
239
240     if let Err(slurs) = slur_check(&data.name) {
241       return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
242     }
243
244     if let Some(description) = &data.description {
245       if let Err(slurs) = slur_check(description) {
246         return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
247       }
248     }
249
250     let user_id = claims.id;
251
252     // Make sure user is an admin
253     if !UserView::read(&conn, user_id)?.admin {
254       return Err(APIError::err("not_an_admin").into());
255     }
256
257     let found_site = Site::read(&conn, 1)?;
258
259     let site_form = SiteForm {
260       name: data.name.to_owned(),
261       description: data.description.to_owned(),
262       creator_id: found_site.creator_id,
263       updated: Some(naive_now()),
264       enable_downvotes: data.enable_downvotes,
265       open_registration: data.open_registration,
266       enable_nsfw: data.enable_nsfw,
267     };
268
269     match Site::update(&conn, 1, &site_form) {
270       Ok(site) => site,
271       Err(_e) => return Err(APIError::err("couldnt_update_site").into()),
272     };
273
274     let site_view = SiteView::read(&conn)?;
275
276     Ok(SiteResponse { site: site_view })
277   }
278 }
279
280 impl Perform<GetSiteResponse> for Oper<GetSite> {
281   fn perform(&self, conn: &PgConnection) -> Result<GetSiteResponse, Error> {
282     let _data: &GetSite = &self.data;
283
284     let site_view = if let Ok(_site) = Site::read(&conn, 1) {
285       Some(SiteView::read(&conn)?)
286     } else if let Some(setup) = Settings::get().setup.as_ref() {
287       let register = Register {
288         username: setup.admin_username.to_owned(),
289         email: setup.admin_email.to_owned(),
290         password: setup.admin_password.to_owned(),
291         password_verify: setup.admin_password.to_owned(),
292         admin: true,
293         show_nsfw: true,
294       };
295       let login_response = Oper::new(register).perform(&conn)?;
296       info!("Admin {} created", setup.admin_username);
297
298       let create_site = CreateSite {
299         name: setup.site_name.to_owned(),
300         description: None,
301         enable_downvotes: false,
302         open_registration: false,
303         enable_nsfw: false,
304         auth: login_response.jwt,
305       };
306       Oper::new(create_site).perform(&conn)?;
307       info!("Site {} created", setup.site_name);
308       Some(SiteView::read(&conn)?)
309     } else {
310       None
311     };
312
313     let mut admins = UserView::admins(&conn)?;
314
315     // Make sure the site creator is the top admin
316     if let Some(site_view) = site_view.to_owned() {
317       let site_creator_id = site_view.creator_id;
318       // TODO investigate why this is sometimes coming back null
319       // Maybe user_.admin isn't being set to true?
320       if let Some(creator_index) = admins.iter().position(|r| r.id == site_creator_id) {
321         let creator_user = admins.remove(creator_index);
322         admins.insert(0, creator_user);
323       }
324     }
325
326     let banned = UserView::banned(&conn)?;
327
328     Ok(GetSiteResponse {
329       site: site_view,
330       admins,
331       banned,
332       online: 0,
333     })
334   }
335 }
336
337 impl Perform<SearchResponse> for Oper<Search> {
338   fn perform(&self, conn: &PgConnection) -> Result<SearchResponse, Error> {
339     let data: &Search = &self.data;
340
341     let user_id: Option<i32> = match &data.auth {
342       Some(auth) => match Claims::decode(&auth) {
343         Ok(claims) => {
344           let user_id = claims.claims.id;
345           Some(user_id)
346         }
347         Err(_e) => None,
348       },
349       None => None,
350     };
351
352     let sort = SortType::from_str(&data.sort)?;
353     let type_ = SearchType::from_str(&data.type_)?;
354
355     let mut posts = Vec::new();
356     let mut comments = Vec::new();
357     let mut communities = Vec::new();
358     let mut users = Vec::new();
359
360     // TODO no clean / non-nsfw searching rn
361
362     match type_ {
363       SearchType::Posts => {
364         posts = PostQueryBuilder::create(&conn)
365           .sort(&sort)
366           .show_nsfw(true)
367           .for_community_id(data.community_id)
368           .search_term(data.q.to_owned())
369           .my_user_id(user_id)
370           .page(data.page)
371           .limit(data.limit)
372           .list()?;
373       }
374       SearchType::Comments => {
375         comments = CommentQueryBuilder::create(&conn)
376           .sort(&sort)
377           .search_term(data.q.to_owned())
378           .my_user_id(user_id)
379           .page(data.page)
380           .limit(data.limit)
381           .list()?;
382       }
383       SearchType::Communities => {
384         communities = CommunityQueryBuilder::create(&conn)
385           .sort(&sort)
386           .search_term(data.q.to_owned())
387           .page(data.page)
388           .limit(data.limit)
389           .list()?;
390       }
391       SearchType::Users => {
392         users = UserQueryBuilder::create(&conn)
393           .sort(&sort)
394           .search_term(data.q.to_owned())
395           .page(data.page)
396           .limit(data.limit)
397           .list()?;
398       }
399       SearchType::All => {
400         posts = PostQueryBuilder::create(&conn)
401           .sort(&sort)
402           .show_nsfw(true)
403           .for_community_id(data.community_id)
404           .search_term(data.q.to_owned())
405           .my_user_id(user_id)
406           .page(data.page)
407           .limit(data.limit)
408           .list()?;
409
410         comments = CommentQueryBuilder::create(&conn)
411           .sort(&sort)
412           .search_term(data.q.to_owned())
413           .my_user_id(user_id)
414           .page(data.page)
415           .limit(data.limit)
416           .list()?;
417
418         communities = CommunityQueryBuilder::create(&conn)
419           .sort(&sort)
420           .search_term(data.q.to_owned())
421           .page(data.page)
422           .limit(data.limit)
423           .list()?;
424
425         users = UserQueryBuilder::create(&conn)
426           .sort(&sort)
427           .search_term(data.q.to_owned())
428           .page(data.page)
429           .limit(data.limit)
430           .list()?;
431       }
432       SearchType::Url => {
433         posts = PostQueryBuilder::create(&conn)
434           .sort(&sort)
435           .show_nsfw(true)
436           .for_community_id(data.community_id)
437           .url_search(data.q.to_owned())
438           .page(data.page)
439           .limit(data.limit)
440           .list()?;
441       }
442     };
443
444     // Return the jwt
445     Ok(SearchResponse {
446       type_: data.type_.to_owned(),
447       comments,
448       posts,
449       communities,
450       users,
451     })
452   }
453 }
454
455 impl Perform<GetSiteResponse> for Oper<TransferSite> {
456   fn perform(&self, conn: &PgConnection) -> Result<GetSiteResponse, Error> {
457     let data: &TransferSite = &self.data;
458
459     let claims = match Claims::decode(&data.auth) {
460       Ok(claims) => claims.claims,
461       Err(_e) => return Err(APIError::err("not_logged_in").into()),
462     };
463
464     let user_id = claims.id;
465
466     let read_site = Site::read(&conn, 1)?;
467
468     // Make sure user is the creator
469     if read_site.creator_id != user_id {
470       return Err(APIError::err("not_an_admin").into());
471     }
472
473     let site_form = SiteForm {
474       name: read_site.name,
475       description: read_site.description,
476       creator_id: data.user_id,
477       updated: Some(naive_now()),
478       enable_downvotes: read_site.enable_downvotes,
479       open_registration: read_site.open_registration,
480       enable_nsfw: read_site.enable_nsfw,
481     };
482
483     match Site::update(&conn, 1, &site_form) {
484       Ok(site) => site,
485       Err(_e) => return Err(APIError::err("couldnt_update_site").into()),
486     };
487
488     // Mod tables
489     let form = ModAddForm {
490       mod_user_id: user_id,
491       other_user_id: data.user_id,
492       removed: Some(false),
493     };
494
495     ModAdd::create(&conn, &form)?;
496
497     let site_view = SiteView::read(&conn)?;
498
499     let mut admins = UserView::admins(&conn)?;
500     let creator_index = admins
501       .iter()
502       .position(|r| r.id == site_view.creator_id)
503       .unwrap();
504     let creator_user = admins.remove(creator_index);
505     admins.insert(0, creator_user);
506
507     let banned = UserView::banned(&conn)?;
508
509     Ok(GetSiteResponse {
510       site: Some(site_view),
511       admins,
512       banned,
513       online: 0,
514     })
515   }
516 }