use activitypub::{ activity::{Create, Follow}, collection::OrderedCollection, object::Article }; use rocket::{ State, request::LenientForm, response::{Redirect, Flash} }; use rocket_contrib::Template; use serde_json; use validator::{Validate, ValidationError}; use workerpool::{Pool, thunk::*}; use plume_common::activity_pub::{ ActivityStream, broadcast, Id, IntoId, ApRequest, inbox::{FromActivity, Notify} }; use plume_common::utils; use plume_models::{ blogs::Blog, db_conn::DbConn, follows, instance::Instance, posts::Post, reshares::Reshare, users::* }; use inbox::Inbox; use routes::Page; #[get("/me")] fn me(user: Option) -> Result> { match user { Some(user) => Ok(Redirect::to(uri!(details: name = user.username))), None => Err(utils::requires_login("", uri!(me))) } } #[get("/@/", rank = 2)] fn details<'r>(name: String, conn: DbConn, account: Option, worker: State>>, other_conn: DbConn) -> Template { may_fail!(account, User::find_by_fqn(&*conn, name), "Couldn't find requested user", |user| { let recents = Post::get_recents_for_author(&*conn, &user, 6); let reshares = Reshare::get_recents_for_author(&*conn, &user, 6); let user_id = user.id.clone(); let n_followers = user.get_followers(&*conn).len(); // Fetch new articles if !user.get_instance(&*conn).local { let user_clone = user.clone(); worker.execute(Thunk::of(move || { for create_act in user_clone.fetch_outbox::() { match create_act.create_props.object_object::
() { Ok(article) => { Post::from_activity(&*other_conn, article, user_clone.clone().into_id()); println!("Fetched article from remote user"); } Err(e) => println!("Error while fetching articles in background: {:?}", e) } } })); } Template::render("users/details", json!({ "user": user.to_json(&*conn), "instance_url": user.get_instance(&*conn).public_domain, "is_remote": user.instance_id != Instance::local_id(&*conn), "follows": account.clone().map(|x| x.is_following(&*conn, user.id)).unwrap_or(false), "account": account, "recents": recents.into_iter().map(|p| p.to_json(&*conn)).collect::>(), "reshares": reshares.into_iter().map(|r| r.get_post(&*conn).unwrap().to_json(&*conn)).collect::>(), "is_self": account.map(|a| a.id == user_id).unwrap_or(false), "n_followers": n_followers })) }) } #[get("/dashboard")] fn dashboard(user: User, conn: DbConn) -> Template { let blogs = Blog::find_for_author(&*conn, user.id); Template::render("users/dashboard", json!({ "account": user, "blogs": blogs })) } #[get("/dashboard", rank = 2)] fn dashboard_auth() -> Flash { utils::requires_login("You need to be logged in order to access your dashboard", uri!(dashboard)) } #[get("/@//follow")] fn follow(name: String, conn: DbConn, user: User, worker: State>>) -> Redirect { let target = User::find_by_fqn(&*conn, name.clone()).unwrap(); let f = follows::Follow::insert(&*conn, follows::NewFollow { follower_id: user.id, following_id: target.id }); f.notify(&*conn); let mut act = Follow::default(); act.follow_props.set_actor_link::(user.clone().into_id()).unwrap(); act.follow_props.set_object_object(user.into_activity(&*conn)).unwrap(); act.object_props.set_id_string(format!("{}/follow/{}", user.ap_url, target.ap_url)).unwrap(); act.object_props.set_to_link(target.clone().into_id()).expect("New Follow error while setting 'to'"); act.object_props.set_cc_link_vec::(vec![]).expect("New Follow error while setting 'cc'"); worker.execute(Thunk::of(move || broadcast(&user, act, vec![target]))); Redirect::to(uri!(details: name = name)) } #[get("/@//follow", rank = 2)] fn follow_auth(name: String) -> Flash { utils::requires_login("You need to be logged in order to follow someone", uri!(follow: name = name)) } #[get("/@//followers?")] fn followers_paginated(name: String, conn: DbConn, account: Option, page: Page) -> Template { may_fail!(account, User::find_by_fqn(&*conn, name.clone()), "Couldn't find requested user", |user| { let user_id = user.id.clone(); let followers_count = user.get_followers(&*conn).len(); Template::render("users/followers", json!({ "user": user.to_json(&*conn), "instance_url": user.get_instance(&*conn).public_domain, "is_remote": user.instance_id != Instance::local_id(&*conn), "follows": account.clone().map(|x| x.is_following(&*conn, user.id)).unwrap_or(false), "followers": user.get_followers_page(&*conn, page.limits()).into_iter().map(|f| f.to_json(&*conn)).collect::>(), "account": account, "is_self": account.map(|a| a.id == user_id).unwrap_or(false), "n_followers": followers_count, "page": page.page, "n_pages": Page::total(followers_count as i32) })) }) } #[get("/@//followers", rank = 2)] fn followers(name: String, conn: DbConn, account: Option) -> Template { followers_paginated(name, conn, account, Page::first()) } #[get("/@/", rank = 1)] fn activity_details(name: String, conn: DbConn, _ap: ApRequest) -> ActivityStream { let user = User::find_local(&*conn, name).unwrap(); ActivityStream::new(user.into_activity(&*conn)) } #[get("/users/new")] fn new(user: Option) -> Template { Template::render("users/new", json!({ "account": user, "errors": null, "form": null })) } #[get("/@//edit")] fn edit(name: String, user: User) -> Option