2 use crate::api::user::Register;
3 use crate::api::{Oper, Perform};
4 use crate::settings::Settings;
5 use diesel::PgConnection;
9 #[derive(Serialize, Deserialize)]
10 pub struct ListCategories {}
12 #[derive(Serialize, Deserialize)]
13 pub struct ListCategoriesResponse {
14 categories: Vec<Category>,
17 #[derive(Serialize, Deserialize)]
21 community_id: Option<i32>,
28 #[derive(Serialize, Deserialize)]
29 pub struct SearchResponse {
31 comments: Vec<CommentView>,
33 communities: Vec<CommunityView>,
37 #[derive(Serialize, Deserialize)]
38 pub struct GetModlog {
39 mod_user_id: Option<i32>,
40 community_id: Option<i32>,
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>,
58 #[derive(Serialize, Deserialize)]
59 pub struct CreateSite {
61 pub description: Option<String>,
62 pub enable_downvotes: bool,
63 pub open_registration: bool,
64 pub enable_nsfw: bool,
68 #[derive(Serialize, Deserialize)]
71 description: Option<String>,
72 enable_downvotes: bool,
73 open_registration: bool,
78 #[derive(Serialize, Deserialize)]
81 #[derive(Serialize, Deserialize)]
82 pub struct SiteResponse {
86 #[derive(Serialize, Deserialize)]
87 pub struct GetSiteResponse {
88 site: Option<SiteView>,
89 admins: Vec<UserView>,
90 banned: Vec<UserView>,
94 #[derive(Serialize, Deserialize)]
95 pub struct TransferSite {
100 #[derive(Serialize, Deserialize)]
101 pub struct GetSiteConfig {
105 #[derive(Serialize, Deserialize)]
106 pub struct GetSiteConfigResponse {
107 config_hjson: String,
110 #[derive(Serialize, Deserialize)]
111 pub struct SaveSiteConfig {
112 config_hjson: String,
116 impl Perform<ListCategoriesResponse> for Oper<ListCategories> {
117 fn perform(&self, conn: &PgConnection) -> Result<ListCategoriesResponse, Error> {
118 let _data: &ListCategories = &self.data;
120 let categories: Vec<Category> = Category::list_all(&conn)?;
123 Ok(ListCategoriesResponse { categories })
127 impl Perform<GetModlogResponse> for Oper<GetModlog> {
128 fn perform(&self, conn: &PgConnection) -> Result<GetModlogResponse, Error> {
129 let data: &GetModlog = &self.data;
131 let removed_posts = ModRemovePostView::list(
138 let locked_posts = ModLockPostView::list(
145 let stickied_posts = ModStickyPostView::list(
152 let removed_comments = ModRemoveCommentView::list(
159 let banned_from_community = ModBanFromCommunityView::list(
166 let added_to_community = ModAddCommunityView::list(
174 // These arrays are only for the full modlog, when a community isn't given
175 let (removed_communities, banned, added) = if data.community_id.is_none() {
177 ModRemoveCommunityView::list(&conn, data.mod_user_id, data.page, data.limit)?,
178 ModBanView::list(&conn, data.mod_user_id, data.page, data.limit)?,
179 ModAddView::list(&conn, data.mod_user_id, data.page, data.limit)?,
182 (Vec::new(), Vec::new(), Vec::new())
186 Ok(GetModlogResponse {
192 banned_from_community,
200 impl Perform<SiteResponse> for Oper<CreateSite> {
201 fn perform(&self, conn: &PgConnection) -> Result<SiteResponse, Error> {
202 let data: &CreateSite = &self.data;
204 let claims = match Claims::decode(&data.auth) {
205 Ok(claims) => claims.claims,
206 Err(_e) => return Err(APIError::err("not_logged_in").into()),
209 if let Err(slurs) = slur_check(&data.name) {
210 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
213 if let Some(description) = &data.description {
214 if let Err(slurs) = slur_check(description) {
215 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
219 let user_id = claims.id;
221 // Make sure user is an admin
222 if !UserView::read(&conn, user_id)?.admin {
223 return Err(APIError::err("not_an_admin").into());
226 let site_form = SiteForm {
227 name: data.name.to_owned(),
228 description: data.description.to_owned(),
230 enable_downvotes: data.enable_downvotes,
231 open_registration: data.open_registration,
232 enable_nsfw: data.enable_nsfw,
236 match Site::create(&conn, &site_form) {
238 Err(_e) => return Err(APIError::err("site_already_exists").into()),
241 let site_view = SiteView::read(&conn)?;
243 Ok(SiteResponse { site: site_view })
247 impl Perform<SiteResponse> for Oper<EditSite> {
248 fn perform(&self, conn: &PgConnection) -> Result<SiteResponse, Error> {
249 let data: &EditSite = &self.data;
251 let claims = match Claims::decode(&data.auth) {
252 Ok(claims) => claims.claims,
253 Err(_e) => return Err(APIError::err("not_logged_in").into()),
256 if let Err(slurs) = slur_check(&data.name) {
257 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
260 if let Some(description) = &data.description {
261 if let Err(slurs) = slur_check(description) {
262 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
266 let user_id = claims.id;
268 // Make sure user is an admin
269 if !UserView::read(&conn, user_id)?.admin {
270 return Err(APIError::err("not_an_admin").into());
273 let found_site = Site::read(&conn, 1)?;
275 let site_form = SiteForm {
276 name: data.name.to_owned(),
277 description: data.description.to_owned(),
278 creator_id: found_site.creator_id,
279 updated: Some(naive_now()),
280 enable_downvotes: data.enable_downvotes,
281 open_registration: data.open_registration,
282 enable_nsfw: data.enable_nsfw,
285 match Site::update(&conn, 1, &site_form) {
287 Err(_e) => return Err(APIError::err("couldnt_update_site").into()),
290 let site_view = SiteView::read(&conn)?;
292 Ok(SiteResponse { site: site_view })
296 impl Perform<GetSiteResponse> for Oper<GetSite> {
297 fn perform(&self, conn: &PgConnection) -> Result<GetSiteResponse, Error> {
298 let _data: &GetSite = &self.data;
300 let site = Site::read(&conn, 1);
301 let site_view = if site.is_ok() {
302 Some(SiteView::read(&conn)?)
303 } else if let Some(setup) = Settings::get().setup.as_ref() {
304 let register = Register {
305 username: setup.admin_username.to_owned(),
306 email: setup.admin_email.to_owned(),
307 password: setup.admin_password.to_owned(),
308 password_verify: setup.admin_password.to_owned(),
312 let login_response = Oper::new(register).perform(&conn)?;
313 info!("Admin {} created", setup.admin_username);
315 let create_site = CreateSite {
316 name: setup.site_name.to_owned(),
318 enable_downvotes: false,
319 open_registration: false,
321 auth: login_response.jwt,
323 Oper::new(create_site).perform(&conn)?;
324 info!("Site {} created", setup.site_name);
325 Some(SiteView::read(&conn)?)
330 let mut admins = UserView::admins(&conn)?;
331 if site_view.is_some() {
332 let site_creator_id = site_view.to_owned().unwrap().creator_id;
333 let creator_index = admins.iter().position(|r| r.id == site_creator_id).unwrap();
334 let creator_user = admins.remove(creator_index);
335 admins.insert(0, creator_user);
338 let banned = UserView::banned(&conn)?;
349 impl Perform<SearchResponse> for Oper<Search> {
350 fn perform(&self, conn: &PgConnection) -> Result<SearchResponse, Error> {
351 let data: &Search = &self.data;
353 let user_id: Option<i32> = match &data.auth {
354 Some(auth) => match Claims::decode(&auth) {
356 let user_id = claims.claims.id;
364 let sort = SortType::from_str(&data.sort)?;
365 let type_ = SearchType::from_str(&data.type_)?;
367 let mut posts = Vec::new();
368 let mut comments = Vec::new();
369 let mut communities = Vec::new();
370 let mut users = Vec::new();
372 // TODO no clean / non-nsfw searching rn
375 SearchType::Posts => {
376 posts = PostQueryBuilder::create(&conn)
379 .for_community_id(data.community_id)
380 .search_term(data.q.to_owned())
386 SearchType::Comments => {
387 comments = CommentQueryBuilder::create(&conn)
389 .search_term(data.q.to_owned())
395 SearchType::Communities => {
396 communities = CommunityQueryBuilder::create(&conn)
398 .search_term(data.q.to_owned())
403 SearchType::Users => {
404 users = UserQueryBuilder::create(&conn)
406 .search_term(data.q.to_owned())
412 posts = PostQueryBuilder::create(&conn)
415 .for_community_id(data.community_id)
416 .search_term(data.q.to_owned())
422 comments = CommentQueryBuilder::create(&conn)
424 .search_term(data.q.to_owned())
430 communities = CommunityQueryBuilder::create(&conn)
432 .search_term(data.q.to_owned())
437 users = UserQueryBuilder::create(&conn)
439 .search_term(data.q.to_owned())
445 posts = PostQueryBuilder::create(&conn)
448 .for_community_id(data.community_id)
449 .url_search(data.q.to_owned())
458 type_: data.type_.to_owned(),
467 impl Perform<GetSiteResponse> for Oper<TransferSite> {
468 fn perform(&self, conn: &PgConnection) -> Result<GetSiteResponse, Error> {
469 let data: &TransferSite = &self.data;
471 let claims = match Claims::decode(&data.auth) {
472 Ok(claims) => claims.claims,
473 Err(_e) => return Err(APIError::err("not_logged_in").into()),
476 let user_id = claims.id;
478 let read_site = Site::read(&conn, 1)?;
480 // Make sure user is the creator
481 if read_site.creator_id != user_id {
482 return Err(APIError::err("not_an_admin").into());
485 let site_form = SiteForm {
486 name: read_site.name,
487 description: read_site.description,
488 creator_id: data.user_id,
489 updated: Some(naive_now()),
490 enable_downvotes: read_site.enable_downvotes,
491 open_registration: read_site.open_registration,
492 enable_nsfw: read_site.enable_nsfw,
495 match Site::update(&conn, 1, &site_form) {
497 Err(_e) => return Err(APIError::err("couldnt_update_site").into()),
501 let form = ModAddForm {
502 mod_user_id: user_id,
503 other_user_id: data.user_id,
504 removed: Some(false),
507 ModAdd::create(&conn, &form)?;
509 let site_view = SiteView::read(&conn)?;
511 let mut admins = UserView::admins(&conn)?;
512 let creator_index = admins
514 .position(|r| r.id == site_view.creator_id)
516 let creator_user = admins.remove(creator_index);
517 admins.insert(0, creator_user);
519 let banned = UserView::banned(&conn)?;
522 site: Some(site_view),
530 impl Perform<GetSiteConfigResponse> for Oper<GetSiteConfig> {
531 fn perform(&self, conn: &PgConnection) -> Result<GetSiteConfigResponse, Error> {
532 let data: &GetSiteConfig = &self.data;
534 let claims = match Claims::decode(&data.auth) {
535 Ok(claims) => claims.claims,
536 Err(_e) => return Err(APIError::err("not_logged_in").into()),
539 let user_id = claims.id;
541 // Only let admins read this
542 let admins = UserView::admins(&conn)?;
543 let admin_ids: Vec<i32> = admins.into_iter().map(|m| m.id).collect();
545 if !admin_ids.contains(&user_id) {
546 return Err(APIError::err("not_an_admin").into());
549 let config_hjson = Settings::read_config_file()?;
551 Ok(GetSiteConfigResponse { config_hjson })
555 impl Perform<GetSiteConfigResponse> for Oper<SaveSiteConfig> {
556 fn perform(&self, conn: &PgConnection) -> Result<GetSiteConfigResponse, Error> {
557 let data: &SaveSiteConfig = &self.data;
559 let claims = match Claims::decode(&data.auth) {
560 Ok(claims) => claims.claims,
561 Err(_e) => return Err(APIError::err("not_logged_in").into()),
564 let user_id = claims.id;
566 // Only let admins read this
567 let admins = UserView::admins(&conn)?;
568 let admin_ids: Vec<i32> = admins.into_iter().map(|m| m.id).collect();
570 if !admin_ids.contains(&user_id) {
571 return Err(APIError::err("not_an_admin").into());
574 // Make sure docker doesn't have :ro at the end of the volume, so its not a read-only filesystem
575 let config_hjson = match Settings::save_config_file(&data.config_hjson) {
576 Ok(config_hjson) => config_hjson,
577 Err(_e) => return Err(APIError::err("couldnt_update_site").into()),
580 Ok(GetSiteConfigResponse { config_hjson })