Avoid panics (#392)
- Use `Result` as much as possible - Display errors instead of panicking TODO (maybe in another PR? this one is already quite big): - Find a way to merge Ructe/ErrorPage types, so that we can have routes returning `Result<X, ErrorPage>` instead of panicking when we have an `Error` - Display more details about the error, to make it easier to debug (sorry, this isn't going to be fun to review, the diff is huge, but it is always the same changes)
This commit is contained in:
+40
-14
@@ -1,15 +1,41 @@
|
||||
use rocket::request::Form;
|
||||
use rocket::{response::{self, Responder}, request::{Form, Request}};
|
||||
use rocket_contrib::json::Json;
|
||||
use serde_json;
|
||||
|
||||
use plume_common::utils::random_hex;
|
||||
use plume_models::{
|
||||
Error,
|
||||
apps::App,
|
||||
api_tokens::*,
|
||||
db_conn::DbConn,
|
||||
users::User,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ApiError(Error);
|
||||
|
||||
impl From<Error> for ApiError {
|
||||
fn from(err: Error) -> ApiError {
|
||||
ApiError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> Responder<'r> for ApiError {
|
||||
fn respond_to(self, req: &Request) -> response::Result<'r> {
|
||||
match self.0 {
|
||||
Error::NotFound => Json(json!({
|
||||
"error": "Not found"
|
||||
})).respond_to(req),
|
||||
Error::Unauthorized => Json(json!({
|
||||
"error": "You are not authorized to access this resource"
|
||||
})).respond_to(req),
|
||||
_ => Json(json!({
|
||||
"error": "Server error"
|
||||
})).respond_to(req)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromForm)]
|
||||
pub struct OAuthRequest {
|
||||
client_id: String,
|
||||
@@ -20,38 +46,38 @@ pub struct OAuthRequest {
|
||||
}
|
||||
|
||||
#[get("/oauth2?<query..>")]
|
||||
pub fn oauth(query: Form<OAuthRequest>, conn: DbConn) -> Json<serde_json::Value> {
|
||||
let app = App::find_by_client_id(&*conn, &query.client_id).expect("OAuth request from unknown client");
|
||||
pub fn oauth(query: Form<OAuthRequest>, conn: DbConn) -> Result<Json<serde_json::Value>, ApiError> {
|
||||
let app = App::find_by_client_id(&*conn, &query.client_id)?;
|
||||
if app.client_secret == query.client_secret {
|
||||
if let Some(user) = User::find_local(&*conn, &query.username) {
|
||||
if let Ok(user) = User::find_local(&*conn, &query.username) {
|
||||
if user.auth(&query.password) {
|
||||
let token = ApiToken::insert(&*conn, NewApiToken {
|
||||
app_id: app.id,
|
||||
user_id: user.id,
|
||||
value: random_hex(),
|
||||
scopes: query.scopes.clone(),
|
||||
});
|
||||
Json(json!({
|
||||
})?;
|
||||
Ok(Json(json!({
|
||||
"token": token.value
|
||||
}))
|
||||
})))
|
||||
} else {
|
||||
Json(json!({
|
||||
Ok(Json(json!({
|
||||
"error": "Invalid credentials"
|
||||
}))
|
||||
})))
|
||||
}
|
||||
} else {
|
||||
// Making fake password verification to avoid different
|
||||
// response times that would make it possible to know
|
||||
// if a username is registered or not.
|
||||
User::get(&*conn, 1).unwrap().auth(&query.password);
|
||||
Json(json!({
|
||||
User::get(&*conn, 1)?.auth(&query.password);
|
||||
Ok(Json(json!({
|
||||
"error": "Invalid credentials"
|
||||
}))
|
||||
})))
|
||||
}
|
||||
} else {
|
||||
Json(json!({
|
||||
Ok(Json(json!({
|
||||
"error": "Invalid client_secret"
|
||||
}))
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+19
-17
@@ -42,13 +42,14 @@ pub trait Inbox {
|
||||
match act["type"].as_str() {
|
||||
Some(t) => match t {
|
||||
"Announce" => {
|
||||
Reshare::from_activity(conn, serde_json::from_value(act.clone())?, actor_id);
|
||||
Reshare::from_activity(conn, serde_json::from_value(act.clone())?, actor_id)
|
||||
.expect("Inbox::received: Announce error");;
|
||||
Ok(())
|
||||
}
|
||||
"Create" => {
|
||||
let act: Create = serde_json::from_value(act.clone())?;
|
||||
if Post::try_from_activity(&(conn, searcher), act.clone())
|
||||
|| Comment::try_from_activity(conn, act)
|
||||
if Post::try_from_activity(&(conn, searcher), act.clone()).is_ok()
|
||||
|| Comment::try_from_activity(conn, act).is_ok()
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
@@ -64,7 +65,7 @@ pub trait Inbox {
|
||||
.id_string()?,
|
||||
actor_id.as_ref(),
|
||||
&(conn, searcher),
|
||||
);
|
||||
).ok();
|
||||
Comment::delete_id(
|
||||
&act.delete_props
|
||||
.object_object::<Tombstone>()?
|
||||
@@ -72,11 +73,12 @@ pub trait Inbox {
|
||||
.id_string()?,
|
||||
actor_id.as_ref(),
|
||||
conn,
|
||||
);
|
||||
).ok();
|
||||
Ok(())
|
||||
}
|
||||
"Follow" => {
|
||||
Follow::from_activity(conn, serde_json::from_value(act.clone())?, actor_id).notify(conn);
|
||||
Follow::from_activity(conn, serde_json::from_value(act.clone())?, actor_id)
|
||||
.and_then(|f| f.notify(conn)).expect("Inbox::received: follow from activity error");;
|
||||
Ok(())
|
||||
}
|
||||
"Like" => {
|
||||
@@ -84,7 +86,7 @@ pub trait Inbox {
|
||||
conn,
|
||||
serde_json::from_value(act.clone())?,
|
||||
actor_id,
|
||||
);
|
||||
).expect("Inbox::received: like from activity error");;
|
||||
Ok(())
|
||||
}
|
||||
"Undo" => {
|
||||
@@ -99,7 +101,7 @@ pub trait Inbox {
|
||||
.id_string()?,
|
||||
actor_id.as_ref(),
|
||||
conn,
|
||||
);
|
||||
).expect("Inbox::received: undo like fail");;
|
||||
Ok(())
|
||||
}
|
||||
"Announce" => {
|
||||
@@ -110,7 +112,7 @@ pub trait Inbox {
|
||||
.id_string()?,
|
||||
actor_id.as_ref(),
|
||||
conn,
|
||||
);
|
||||
).expect("Inbox::received: undo reshare fail");;
|
||||
Ok(())
|
||||
}
|
||||
"Follow" => {
|
||||
@@ -121,21 +123,21 @@ pub trait Inbox {
|
||||
.id_string()?,
|
||||
actor_id.as_ref(),
|
||||
conn,
|
||||
);
|
||||
).expect("Inbox::received: undo follow error");;
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InboxError::CantUndo)?,
|
||||
}
|
||||
} else {
|
||||
let link = act.undo_props.object.as_str().expect("Inbox::received: undo don't contain type and isn't Link");
|
||||
if let Some(like) = likes::Like::find_by_ap_url(conn, link) {
|
||||
likes::Like::delete_id(&like.ap_url, actor_id.as_ref(), conn);
|
||||
if let Ok(like) = likes::Like::find_by_ap_url(conn, link) {
|
||||
likes::Like::delete_id(&like.ap_url, actor_id.as_ref(), conn).expect("Inbox::received: delete Like error");
|
||||
Ok(())
|
||||
} else if let Some(reshare) = Reshare::find_by_ap_url(conn, link) {
|
||||
Reshare::delete_id(&reshare.ap_url, actor_id.as_ref(), conn);
|
||||
} else if let Ok(reshare) = Reshare::find_by_ap_url(conn, link) {
|
||||
Reshare::delete_id(&reshare.ap_url, actor_id.as_ref(), conn).expect("Inbox::received: delete Announce error");
|
||||
Ok(())
|
||||
} else if let Some(follow) = Follow::find_by_ap_url(conn, link) {
|
||||
Follow::delete_id(&follow.ap_url, actor_id.as_ref(), conn);
|
||||
} else if let Ok(follow) = Follow::find_by_ap_url(conn, link) {
|
||||
Follow::delete_id(&follow.ap_url, actor_id.as_ref(), conn).expect("Inbox::received: delete Follow error");
|
||||
Ok(())
|
||||
} else {
|
||||
Err(InboxError::NoType)?
|
||||
@@ -144,7 +146,7 @@ pub trait Inbox {
|
||||
}
|
||||
"Update" => {
|
||||
let act: Update = serde_json::from_value(act.clone())?;
|
||||
Post::handle_update(conn, &act.update_props.object_object()?, searcher);
|
||||
Post::handle_update(conn, &act.update_props.object_object()?, searcher).expect("Inbox::received: post update error");;
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InboxError::InvalidType)?,
|
||||
|
||||
+20
-4
@@ -38,8 +38,11 @@ extern crate webfinger;
|
||||
use diesel::r2d2::ConnectionManager;
|
||||
use rocket::State;
|
||||
use rocket_csrf::CsrfFairingBuilder;
|
||||
use plume_models::{DATABASE_URL, Connection,
|
||||
db_conn::{DbPool, PragmaForeignKey}, search::Searcher as UnmanagedSearcher};
|
||||
use plume_models::{
|
||||
DATABASE_URL, Connection, Error,
|
||||
db_conn::{DbPool, PragmaForeignKey},
|
||||
search::{Searcher as UnmanagedSearcher, SearcherError},
|
||||
};
|
||||
use scheduled_thread_pool::ScheduledThreadPool;
|
||||
use std::process::exit;
|
||||
use std::sync::Arc;
|
||||
@@ -65,10 +68,23 @@ fn init_pool() -> Option<DbPool> {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
||||
let dbpool = init_pool().expect("main: database pool initialization error");
|
||||
let workpool = ScheduledThreadPool::with_name("worker {}", num_cpus::get());
|
||||
let searcher = Arc::new(UnmanagedSearcher::open(&"search_index").unwrap());
|
||||
let searcher = match UnmanagedSearcher::open(&"search_index") {
|
||||
Err(Error::Search(e)) => match e {
|
||||
SearcherError::WriteLockAcquisitionError => panic!(
|
||||
r#"Your search index is locked. Plume can't start. To fix this issue
|
||||
make sure no other Plume instance is started, and run:
|
||||
|
||||
plm search unlock
|
||||
|
||||
Then try to restart Plume.
|
||||
"#),
|
||||
e => Err(e).unwrap()
|
||||
},
|
||||
Err(_) => panic!("Unexpected error while opening search index"),
|
||||
Ok(s) => Arc::new(s)
|
||||
};
|
||||
|
||||
let commiter = searcher.clone();
|
||||
workpool.execute_with_fixed_delay(Duration::from_secs(5), Duration::from_secs(60*30), move || commiter.commit());
|
||||
|
||||
+27
-29
@@ -19,18 +19,17 @@ use plume_models::{
|
||||
posts::Post,
|
||||
users::User
|
||||
};
|
||||
use routes::Page;
|
||||
use routes::{Page, errors::ErrorPage};
|
||||
use template_utils::Ructe;
|
||||
use Searcher;
|
||||
|
||||
#[get("/~/<name>?<page>", rank = 2)]
|
||||
pub fn details(intl: I18n, name: String, conn: DbConn, user: Option<User>, page: Option<Page>) -> Result<Ructe, Ructe> {
|
||||
pub fn details(intl: I18n, name: String, conn: DbConn, user: Option<User>, page: Option<Page>) -> Result<Ructe, ErrorPage> {
|
||||
let page = page.unwrap_or_default();
|
||||
let blog = Blog::find_by_fqn(&*conn, &name)
|
||||
.ok_or_else(|| render!(errors::not_found(&(&*conn, &intl.catalog, user.clone()))))?;
|
||||
let posts = Post::blog_page(&*conn, &blog, page.limits());
|
||||
let articles_count = Post::count_for_blog(&*conn, &blog);
|
||||
let authors = &blog.list_authors(&*conn);
|
||||
let blog = Blog::find_by_fqn(&*conn, &name)?;
|
||||
let posts = Post::blog_page(&*conn, &blog, page.limits())?;
|
||||
let articles_count = Post::count_for_blog(&*conn, &blog)?;
|
||||
let authors = &blog.list_authors(&*conn)?;
|
||||
|
||||
Ok(render!(blogs::details(
|
||||
&(&*conn, &intl.catalog, user.clone()),
|
||||
@@ -40,15 +39,15 @@ pub fn details(intl: I18n, name: String, conn: DbConn, user: Option<User>, page:
|
||||
articles_count,
|
||||
page.0,
|
||||
Page::total(articles_count as i32),
|
||||
user.map(|x| x.is_author_in(&*conn, &blog)).unwrap_or(false),
|
||||
user.and_then(|x| x.is_author_in(&*conn, &blog).ok()).unwrap_or(false),
|
||||
posts
|
||||
)))
|
||||
}
|
||||
|
||||
#[get("/~/<name>", rank = 1)]
|
||||
pub fn activity_details(name: String, conn: DbConn, _ap: ApRequest) -> Option<ActivityStream<CustomGroup>> {
|
||||
let blog = Blog::find_local(&*conn, &name)?;
|
||||
Some(ActivityStream::new(blog.to_activity(&*conn)))
|
||||
let blog = Blog::find_local(&*conn, &name).ok()?;
|
||||
Some(ActivityStream::new(blog.to_activity(&*conn).ok()?))
|
||||
}
|
||||
|
||||
#[get("/blogs/new")]
|
||||
@@ -91,7 +90,7 @@ pub fn create(conn: DbConn, form: LenientForm<NewBlogForm>, user: User, intl: I1
|
||||
Ok(_) => ValidationErrors::new(),
|
||||
Err(e) => e
|
||||
};
|
||||
if Blog::find_local(&*conn, &slug).is_some() {
|
||||
if Blog::find_local(&*conn, &slug).is_ok() {
|
||||
errors.add("title", ValidationError {
|
||||
code: Cow::from("existing_slug"),
|
||||
message: Some(Cow::from("A blog with the same name already exists.")),
|
||||
@@ -104,19 +103,19 @@ pub fn create(conn: DbConn, form: LenientForm<NewBlogForm>, user: User, intl: I1
|
||||
slug.clone(),
|
||||
form.title.to_string(),
|
||||
String::from(""),
|
||||
Instance::local_id(&*conn)
|
||||
));
|
||||
blog.update_boxes(&*conn);
|
||||
Instance::get_local(&*conn).expect("blog::create: instance error").id
|
||||
).expect("blog::create: new local error")).expect("blog::create: error");
|
||||
blog.update_boxes(&*conn).expect("blog::create: insert error");
|
||||
|
||||
BlogAuthor::insert(&*conn, NewBlogAuthor {
|
||||
blog_id: blog.id,
|
||||
author_id: user.id,
|
||||
is_owner: true
|
||||
});
|
||||
}).expect("blog::create: author error");
|
||||
|
||||
Ok(Redirect::to(uri!(details: name = slug.clone(), page = _)))
|
||||
} else {
|
||||
Err(render!(blogs::new(
|
||||
Err(render!(blogs::new(
|
||||
&(&*conn, &intl.catalog, Some(user)),
|
||||
&*form,
|
||||
errors
|
||||
@@ -125,38 +124,37 @@ pub fn create(conn: DbConn, form: LenientForm<NewBlogForm>, user: User, intl: I1
|
||||
}
|
||||
|
||||
#[post("/~/<name>/delete")]
|
||||
pub fn delete(conn: DbConn, name: String, user: Option<User>, intl: I18n, searcher: Searcher) -> Result<Redirect, Option<Ructe>>{
|
||||
let blog = Blog::find_local(&*conn, &name).ok_or(None)?;
|
||||
if user.clone().map(|u| u.is_author_in(&*conn, &blog)).unwrap_or(false) {
|
||||
blog.delete(&conn, &searcher);
|
||||
pub fn delete(conn: DbConn, name: String, user: Option<User>, intl: I18n, searcher: Searcher) -> Result<Redirect, Ructe>{
|
||||
let blog = Blog::find_local(&*conn, &name).expect("blog::delete: blog not found");
|
||||
if user.clone().and_then(|u| u.is_author_in(&*conn, &blog).ok()).unwrap_or(false) {
|
||||
blog.delete(&conn, &searcher).expect("blog::expect: deletion error");
|
||||
Ok(Redirect::to(uri!(super::instance::index)))
|
||||
} else {
|
||||
// TODO actually return 403 error code
|
||||
Err(Some(render!(errors::not_authorized(
|
||||
Err(render!(errors::not_authorized(
|
||||
&(&*conn, &intl.catalog, user),
|
||||
"You are not allowed to delete this blog."
|
||||
))))
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/~/<name>/outbox")]
|
||||
pub fn outbox(name: String, conn: DbConn) -> Option<ActivityStream<OrderedCollection>> {
|
||||
let blog = Blog::find_local(&*conn, &name)?;
|
||||
Some(blog.outbox(&*conn))
|
||||
let blog = Blog::find_local(&*conn, &name).ok()?;
|
||||
Some(blog.outbox(&*conn).ok()?)
|
||||
}
|
||||
|
||||
#[get("/~/<name>/atom.xml")]
|
||||
pub fn atom_feed(name: String, conn: DbConn) -> Option<Content<String>> {
|
||||
let blog = Blog::find_by_fqn(&*conn, &name)?;
|
||||
let blog = Blog::find_by_fqn(&*conn, &name).ok()?;
|
||||
let feed = FeedBuilder::default()
|
||||
.title(blog.title.clone())
|
||||
.id(Instance::get_local(&*conn).expect("blogs::atom_feed: local instance not found error")
|
||||
.id(Instance::get_local(&*conn).ok()?
|
||||
.compute_box("~", &name, "atom.xml"))
|
||||
.entries(Post::get_recents_for_blog(&*conn, &blog, 15)
|
||||
.entries(Post::get_recents_for_blog(&*conn, &blog, 15).ok()?
|
||||
.into_iter()
|
||||
.map(|p| super::post_to_atom(p, &*conn))
|
||||
.collect::<Vec<Entry>>())
|
||||
.build()
|
||||
.expect("blogs::atom_feed: feed creation error");
|
||||
.build().ok()?;
|
||||
Some(Content(ContentType::new("application", "atom+xml"), feed.to_string()))
|
||||
}
|
||||
|
||||
+39
-26
@@ -21,6 +21,7 @@ use plume_models::{
|
||||
users::User
|
||||
};
|
||||
use Worker;
|
||||
use routes::errors::ErrorPage;
|
||||
|
||||
#[derive(Default, FromForm, Debug, Validate, Serialize)]
|
||||
pub struct NewCommentForm {
|
||||
@@ -32,12 +33,15 @@ pub struct NewCommentForm {
|
||||
|
||||
#[post("/~/<blog_name>/<slug>/comment", data = "<form>")]
|
||||
pub fn create(blog_name: String, slug: String, form: LenientForm<NewCommentForm>, user: User, conn: DbConn, worker: Worker, intl: I18n)
|
||||
-> Result<Redirect, Option<Ructe>> {
|
||||
let blog = Blog::find_by_fqn(&*conn, &blog_name).ok_or(None)?;
|
||||
let post = Post::find_by_slug(&*conn, &slug, blog.id).ok_or(None)?;
|
||||
-> Result<Redirect, Ructe> {
|
||||
let blog = Blog::find_by_fqn(&*conn, &blog_name).expect("comments::create: blog error");
|
||||
let post = Post::find_by_slug(&*conn, &slug, blog.id).expect("comments::create: post error");
|
||||
form.validate()
|
||||
.map(|_| {
|
||||
let (html, mentions, _hashtags) = utils::md_to_html(form.content.as_ref(), &Instance::get_local(&conn).expect("comments::create: Error getting local instance").public_domain);
|
||||
let (html, mentions, _hashtags) = utils::md_to_html(
|
||||
form.content.as_ref(),
|
||||
&Instance::get_local(&conn).expect("comments::create: local instance error").public_domain
|
||||
);
|
||||
let comm = Comment::insert(&*conn, NewComment {
|
||||
content: SafeString::new(html.as_ref()),
|
||||
in_response_to_id: form.responding_to,
|
||||
@@ -47,16 +51,22 @@ pub fn create(blog_name: String, slug: String, form: LenientForm<NewCommentForm>
|
||||
sensitive: !form.warning.is_empty(),
|
||||
spoiler_text: form.warning.clone(),
|
||||
public_visibility: true
|
||||
}).update_ap_url(&*conn);
|
||||
let new_comment = comm.create_activity(&*conn);
|
||||
}).expect("comments::create: insert error").update_ap_url(&*conn).expect("comments::create: update ap url error");
|
||||
let new_comment = comm.create_activity(&*conn).expect("comments::create: activity error");
|
||||
|
||||
// save mentions
|
||||
for ment in mentions {
|
||||
Mention::from_activity(&*conn, &Mention::build_activity(&*conn, &ment), post.id, true, true);
|
||||
Mention::from_activity(
|
||||
&*conn,
|
||||
&Mention::build_activity(&*conn, &ment).expect("comments::create: build mention error"),
|
||||
post.id,
|
||||
true,
|
||||
true
|
||||
).expect("comments::create: mention save error");
|
||||
}
|
||||
|
||||
// federate
|
||||
let dest = User::one_by_instance(&*conn);
|
||||
let dest = User::one_by_instance(&*conn).expect("comments::create: dest error");
|
||||
let user_clone = user.clone();
|
||||
worker.execute(move || broadcast(&user_clone, new_comment, dest));
|
||||
|
||||
@@ -64,43 +74,46 @@ pub fn create(blog_name: String, slug: String, form: LenientForm<NewCommentForm>
|
||||
})
|
||||
.map_err(|errors| {
|
||||
// TODO: de-duplicate this code
|
||||
let comments = CommentTree::from_post(&*conn, &post, Some(&user));
|
||||
let comments = CommentTree::from_post(&*conn, &post, Some(&user)).expect("comments::create: comments error");
|
||||
|
||||
let previous = form.responding_to.map(|r| Comment::get(&*conn, r)
|
||||
.expect("comments::create: Error retrieving previous comment"));
|
||||
let previous = form.responding_to.and_then(|r| Comment::get(&*conn, r).ok());
|
||||
|
||||
Some(render!(posts::details(
|
||||
render!(posts::details(
|
||||
&(&*conn, &intl.catalog, Some(user.clone())),
|
||||
post.clone(),
|
||||
blog,
|
||||
&*form,
|
||||
errors,
|
||||
Tag::for_post(&*conn, post.id),
|
||||
Tag::for_post(&*conn, post.id).expect("comments::create: tags error"),
|
||||
comments,
|
||||
previous,
|
||||
post.count_likes(&*conn),
|
||||
post.count_reshares(&*conn),
|
||||
user.has_liked(&*conn, &post),
|
||||
user.has_reshared(&*conn, &post),
|
||||
user.is_following(&*conn, post.get_authors(&*conn)[0].id),
|
||||
post.get_authors(&*conn)[0].clone()
|
||||
)))
|
||||
post.count_likes(&*conn).expect("comments::create: count likes error"),
|
||||
post.count_reshares(&*conn).expect("comments::create: count reshares error"),
|
||||
user.has_liked(&*conn, &post).expect("comments::create: liked error"),
|
||||
user.has_reshared(&*conn, &post).expect("comments::create: reshared error"),
|
||||
user.is_following(&*conn, post.get_authors(&*conn).expect("comments::create: authors error")[0].id)
|
||||
.expect("comments::create: following error"),
|
||||
post.get_authors(&*conn).expect("comments::create: authors error")[0].clone()
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
#[post("/~/<blog>/<slug>/comment/<id>/delete")]
|
||||
pub fn delete(blog: String, slug: String, id: i32, user: User, conn: DbConn, worker: Worker) -> Redirect {
|
||||
if let Some(comment) = Comment::get(&*conn, id) {
|
||||
pub fn delete(blog: String, slug: String, id: i32, user: User, conn: DbConn, worker: Worker) -> Result<Redirect, ErrorPage> {
|
||||
if let Ok(comment) = Comment::get(&*conn, id) {
|
||||
if comment.author_id == user.id {
|
||||
let dest = User::one_by_instance(&*conn);
|
||||
let delete_activity = comment.delete(&*conn);
|
||||
let dest = User::one_by_instance(&*conn)?;
|
||||
let delete_activity = comment.delete(&*conn)?;
|
||||
worker.execute(move || broadcast(&user, delete_activity, dest));
|
||||
}
|
||||
}
|
||||
Redirect::to(uri!(super::posts::details: blog = blog, slug = slug, responding_to = _))
|
||||
Ok(Redirect::to(uri!(super::posts::details: blog = blog, slug = slug, responding_to = _)))
|
||||
}
|
||||
|
||||
#[get("/~/<_blog>/<_slug>/comment/<id>")]
|
||||
pub fn activity_pub(_blog: String, _slug: String, id: i32, _ap: ApRequest, conn: DbConn) -> Option<ActivityStream<Note>> {
|
||||
Comment::get(&*conn, id).map(|c| ActivityStream::new(c.to_activity(&*conn)))
|
||||
Comment::get(&*conn, id)
|
||||
.and_then(|c| c.to_activity(&*conn))
|
||||
.ok()
|
||||
.map(ActivityStream::new)
|
||||
}
|
||||
|
||||
+35
-3
@@ -1,10 +1,42 @@
|
||||
use rocket::Request;
|
||||
use rocket::request::FromRequest;
|
||||
use rocket::{
|
||||
Request,
|
||||
request::FromRequest,
|
||||
response::{self, Responder},
|
||||
};
|
||||
use rocket_i18n::I18n;
|
||||
use plume_models::db_conn::DbConn;
|
||||
use plume_models::{Error, db_conn::DbConn};
|
||||
use plume_models::users::User;
|
||||
use template_utils::Ructe;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ErrorPage(Error);
|
||||
|
||||
impl From<Error> for ErrorPage {
|
||||
fn from(err: Error) -> ErrorPage {
|
||||
ErrorPage(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> Responder<'r> for ErrorPage {
|
||||
fn respond_to(self, req: &Request) -> response::Result<'r> {
|
||||
let conn = req.guard::<DbConn>().succeeded();
|
||||
let intl = req.guard::<I18n>().succeeded();
|
||||
let user = User::from_request(req).succeeded();
|
||||
|
||||
match self.0 {
|
||||
Error::NotFound => render!(errors::not_found(
|
||||
&(&*conn.unwrap(), &intl.unwrap().catalog, user)
|
||||
)).respond_to(req),
|
||||
Error::Unauthorized => render!(errors::not_found(
|
||||
&(&*conn.unwrap(), &intl.unwrap().catalog, user)
|
||||
)).respond_to(req),
|
||||
_ => render!(errors::not_found(
|
||||
&(&*conn.unwrap(), &intl.unwrap().catalog, user)
|
||||
)).respond_to(req)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[catch(404)]
|
||||
pub fn not_found(req: &Request) -> Ructe {
|
||||
let conn = req.guard::<DbConn>().succeeded();
|
||||
|
||||
+87
-95
@@ -17,86 +17,78 @@ use plume_models::{
|
||||
instance::*
|
||||
};
|
||||
use inbox::{Inbox, SignedJson};
|
||||
use routes::Page;
|
||||
use routes::{Page, errors::ErrorPage};
|
||||
use template_utils::Ructe;
|
||||
use Searcher;
|
||||
|
||||
#[get("/")]
|
||||
pub fn index(conn: DbConn, user: Option<User>, intl: I18n) -> Ructe {
|
||||
match Instance::get_local(&*conn) {
|
||||
Some(inst) => {
|
||||
let federated = Post::get_recents_page(&*conn, Page::default().limits());
|
||||
let local = Post::get_instance_page(&*conn, inst.id, Page::default().limits());
|
||||
let user_feed = user.clone().map(|user| {
|
||||
let followed = user.get_following(&*conn);
|
||||
let mut in_feed = followed.into_iter().map(|u| u.id).collect::<Vec<i32>>();
|
||||
in_feed.push(user.id);
|
||||
Post::user_feed_page(&*conn, in_feed, Page::default().limits())
|
||||
});
|
||||
pub fn index(conn: DbConn, user: Option<User>, intl: I18n) -> Result<Ructe, ErrorPage> {
|
||||
let inst = Instance::get_local(&*conn)?;
|
||||
let federated = Post::get_recents_page(&*conn, Page::default().limits())?;
|
||||
let local = Post::get_instance_page(&*conn, inst.id, Page::default().limits())?;
|
||||
let user_feed = user.clone().and_then(|user| {
|
||||
let followed = user.get_following(&*conn).ok()?;
|
||||
let mut in_feed = followed.into_iter().map(|u| u.id).collect::<Vec<i32>>();
|
||||
in_feed.push(user.id);
|
||||
Post::user_feed_page(&*conn, in_feed, Page::default().limits()).ok()
|
||||
});
|
||||
|
||||
render!(instance::index(
|
||||
&(&*conn, &intl.catalog, user),
|
||||
inst,
|
||||
User::count_local(&*conn),
|
||||
Post::count_local(&*conn),
|
||||
local,
|
||||
federated,
|
||||
user_feed
|
||||
))
|
||||
}
|
||||
None => {
|
||||
render!(errors::server_error(
|
||||
&(&*conn, &intl.catalog, user)
|
||||
))
|
||||
}
|
||||
}
|
||||
Ok(render!(instance::index(
|
||||
&(&*conn, &intl.catalog, user),
|
||||
inst,
|
||||
User::count_local(&*conn)?,
|
||||
Post::count_local(&*conn)?,
|
||||
local,
|
||||
federated,
|
||||
user_feed
|
||||
)))
|
||||
}
|
||||
|
||||
#[get("/local?<page>")]
|
||||
pub fn local(conn: DbConn, user: Option<User>, page: Option<Page>, intl: I18n) -> Ructe {
|
||||
pub fn local(conn: DbConn, user: Option<User>, page: Option<Page>, intl: I18n) -> Result<Ructe, ErrorPage> {
|
||||
let page = page.unwrap_or_default();
|
||||
let instance = Instance::get_local(&*conn).expect("instance::paginated_local: local instance not found error");
|
||||
let articles = Post::get_instance_page(&*conn, instance.id, page.limits());
|
||||
render!(instance::local(
|
||||
let instance = Instance::get_local(&*conn)?;
|
||||
let articles = Post::get_instance_page(&*conn, instance.id, page.limits())?;
|
||||
Ok(render!(instance::local(
|
||||
&(&*conn, &intl.catalog, user),
|
||||
instance,
|
||||
articles,
|
||||
page.0,
|
||||
Page::total(Post::count_local(&*conn) as i32)
|
||||
))
|
||||
Page::total(Post::count_local(&*conn)? as i32)
|
||||
)))
|
||||
}
|
||||
|
||||
#[get("/feed?<page>")]
|
||||
pub fn feed(conn: DbConn, user: User, page: Option<Page>, intl: I18n) -> Ructe {
|
||||
pub fn feed(conn: DbConn, user: User, page: Option<Page>, intl: I18n) -> Result<Ructe, ErrorPage> {
|
||||
let page = page.unwrap_or_default();
|
||||
let followed = user.get_following(&*conn);
|
||||
let followed = user.get_following(&*conn)?;
|
||||
let mut in_feed = followed.into_iter().map(|u| u.id).collect::<Vec<i32>>();
|
||||
in_feed.push(user.id);
|
||||
let articles = Post::user_feed_page(&*conn, in_feed, page.limits());
|
||||
render!(instance::feed(
|
||||
let articles = Post::user_feed_page(&*conn, in_feed, page.limits())?;
|
||||
Ok(render!(instance::feed(
|
||||
&(&*conn, &intl.catalog, Some(user)),
|
||||
articles,
|
||||
page.0,
|
||||
Page::total(Post::count_local(&*conn) as i32)
|
||||
))
|
||||
Page::total(Post::count_local(&*conn)? as i32)
|
||||
)))
|
||||
}
|
||||
|
||||
#[get("/federated?<page>")]
|
||||
pub fn federated(conn: DbConn, user: Option<User>, page: Option<Page>, intl: I18n) -> Ructe {
|
||||
pub fn federated(conn: DbConn, user: Option<User>, page: Option<Page>, intl: I18n) -> Result<Ructe, ErrorPage> {
|
||||
let page = page.unwrap_or_default();
|
||||
let articles = Post::get_recents_page(&*conn, page.limits());
|
||||
render!(instance::federated(
|
||||
let articles = Post::get_recents_page(&*conn, page.limits())?;
|
||||
Ok(render!(instance::federated(
|
||||
&(&*conn, &intl.catalog, user),
|
||||
articles,
|
||||
page.0,
|
||||
Page::total(Post::count_local(&*conn) as i32)
|
||||
))
|
||||
Page::total(Post::count_local(&*conn)? as i32)
|
||||
)))
|
||||
}
|
||||
|
||||
#[get("/admin")]
|
||||
pub fn admin(conn: DbConn, admin: Admin, intl: I18n) -> Ructe {
|
||||
let local_inst = Instance::get_local(&*conn).expect("instance::admin: local instance not found");
|
||||
render!(instance::admin(
|
||||
pub fn admin(conn: DbConn, admin: Admin, intl: I18n) -> Result<Ructe, ErrorPage> {
|
||||
let local_inst = Instance::get_local(&*conn)?;
|
||||
Ok(render!(instance::admin(
|
||||
&(&*conn, &intl.catalog, Some(admin.0)),
|
||||
local_inst.clone(),
|
||||
InstanceSettingsForm {
|
||||
@@ -107,7 +99,7 @@ pub fn admin(conn: DbConn, admin: Admin, intl: I18n) -> Ructe {
|
||||
default_license: local_inst.default_license,
|
||||
},
|
||||
ValidationErrors::default()
|
||||
))
|
||||
)))
|
||||
}
|
||||
|
||||
#[derive(Clone, FromForm, Validate, Serialize)]
|
||||
@@ -124,65 +116,65 @@ pub struct InstanceSettingsForm {
|
||||
#[post("/admin", data = "<form>")]
|
||||
pub fn update_settings(conn: DbConn, admin: Admin, form: LenientForm<InstanceSettingsForm>, intl: I18n) -> Result<Redirect, Ructe> {
|
||||
form.validate()
|
||||
.map(|_| {
|
||||
let instance = Instance::get_local(&*conn).expect("instance::update_settings: local instance not found error");
|
||||
.and_then(|_| {
|
||||
let instance = Instance::get_local(&*conn).expect("instance::update_settings: local instance error");
|
||||
instance.update(&*conn,
|
||||
form.name.clone(),
|
||||
form.open_registrations,
|
||||
form.short_description.clone(),
|
||||
form.long_description.clone());
|
||||
Redirect::to(uri!(admin))
|
||||
form.long_description.clone()).expect("instance::update_settings: save error");
|
||||
Ok(Redirect::to(uri!(admin)))
|
||||
})
|
||||
.map_err(|e| {
|
||||
let local_inst = Instance::get_local(&*conn).expect("instance::update_settings: local instance not found");
|
||||
render!(instance::admin(
|
||||
.or_else(|e| {
|
||||
let local_inst = Instance::get_local(&*conn).expect("instance::update_settings: local instance error");
|
||||
Err(render!(instance::admin(
|
||||
&(&*conn, &intl.catalog, Some(admin.0)),
|
||||
local_inst,
|
||||
form.clone(),
|
||||
e
|
||||
))
|
||||
)))
|
||||
})
|
||||
}
|
||||
|
||||
#[get("/admin/instances?<page>")]
|
||||
pub fn admin_instances(admin: Admin, conn: DbConn, page: Option<Page>, intl: I18n) -> Ructe {
|
||||
pub fn admin_instances(admin: Admin, conn: DbConn, page: Option<Page>, intl: I18n) -> Result<Ructe, ErrorPage> {
|
||||
let page = page.unwrap_or_default();
|
||||
let instances = Instance::page(&*conn, page.limits());
|
||||
render!(instance::list(
|
||||
let instances = Instance::page(&*conn, page.limits())?;
|
||||
Ok(render!(instance::list(
|
||||
&(&*conn, &intl.catalog, Some(admin.0)),
|
||||
Instance::get_local(&*conn).expect("admin_instances: local instance error"),
|
||||
Instance::get_local(&*conn)?,
|
||||
instances,
|
||||
page.0,
|
||||
Page::total(Instance::count(&*conn) as i32)
|
||||
))
|
||||
Page::total(Instance::count(&*conn)? as i32)
|
||||
)))
|
||||
}
|
||||
|
||||
#[post("/admin/instances/<id>/block")]
|
||||
pub fn toggle_block(_admin: Admin, conn: DbConn, id: i32) -> Redirect {
|
||||
if let Some(inst) = Instance::get(&*conn, id) {
|
||||
inst.toggle_block(&*conn);
|
||||
pub fn toggle_block(_admin: Admin, conn: DbConn, id: i32) -> Result<Redirect, ErrorPage> {
|
||||
if let Ok(inst) = Instance::get(&*conn, id) {
|
||||
inst.toggle_block(&*conn)?;
|
||||
}
|
||||
|
||||
Redirect::to(uri!(admin_instances: page = _))
|
||||
Ok(Redirect::to(uri!(admin_instances: page = _)))
|
||||
}
|
||||
|
||||
#[get("/admin/users?<page>")]
|
||||
pub fn admin_users(admin: Admin, conn: DbConn, page: Option<Page>, intl: I18n) -> Ructe {
|
||||
pub fn admin_users(admin: Admin, conn: DbConn, page: Option<Page>, intl: I18n) -> Result<Ructe, ErrorPage> {
|
||||
let page = page.unwrap_or_default();
|
||||
render!(instance::users(
|
||||
Ok(render!(instance::users(
|
||||
&(&*conn, &intl.catalog, Some(admin.0)),
|
||||
User::get_local_page(&*conn, page.limits()),
|
||||
User::get_local_page(&*conn, page.limits())?,
|
||||
page.0,
|
||||
Page::total(User::count_local(&*conn) as i32)
|
||||
))
|
||||
Page::total(User::count_local(&*conn)? as i32)
|
||||
)))
|
||||
}
|
||||
|
||||
#[post("/admin/users/<id>/ban")]
|
||||
pub fn ban(_admin: Admin, conn: DbConn, id: i32, searcher: Searcher) -> Redirect {
|
||||
if let Some(u) = User::get(&*conn, id) {
|
||||
u.delete(&*conn, &searcher);
|
||||
pub fn ban(_admin: Admin, conn: DbConn, id: i32, searcher: Searcher) -> Result<Redirect, ErrorPage> {
|
||||
if let Ok(u) = User::get(&*conn, id) {
|
||||
u.delete(&*conn, &searcher)?;
|
||||
}
|
||||
Redirect::to(uri!(admin_users: page = _))
|
||||
Ok(Redirect::to(uri!(admin_users: page = _)))
|
||||
}
|
||||
|
||||
#[post("/inbox", data = "<data>")]
|
||||
@@ -200,7 +192,7 @@ pub fn shared_inbox(conn: DbConn, data: SignedJson<serde_json::Value>, headers:
|
||||
return Err(status::BadRequest(Some("Invalid signature")));
|
||||
}
|
||||
|
||||
if Instance::is_blocked(&*conn, actor_id) {
|
||||
if Instance::is_blocked(&*conn, actor_id).map_err(|_| status::BadRequest(Some("Can't tell if instance is blocked")))? {
|
||||
return Ok(String::new());
|
||||
}
|
||||
let instance = Instance::get_local(&*conn).expect("instance::shared_inbox: local instance not found error");
|
||||
@@ -214,8 +206,8 @@ pub fn shared_inbox(conn: DbConn, data: SignedJson<serde_json::Value>, headers:
|
||||
}
|
||||
|
||||
#[get("/nodeinfo")]
|
||||
pub fn nodeinfo(conn: DbConn) -> Json<serde_json::Value> {
|
||||
Json(json!({
|
||||
pub fn nodeinfo(conn: DbConn) -> Result<Json<serde_json::Value>, ErrorPage> {
|
||||
Ok(Json(json!({
|
||||
"version": "2.0",
|
||||
"software": {
|
||||
"name": "Plume",
|
||||
@@ -229,31 +221,31 @@ pub fn nodeinfo(conn: DbConn) -> Json<serde_json::Value> {
|
||||
"openRegistrations": true,
|
||||
"usage": {
|
||||
"users": {
|
||||
"total": User::count_local(&*conn)
|
||||
"total": User::count_local(&*conn)?
|
||||
},
|
||||
"localPosts": Post::count_local(&*conn),
|
||||
"localComments": Comment::count_local(&*conn)
|
||||
"localPosts": Post::count_local(&*conn)?,
|
||||
"localComments": Comment::count_local(&*conn)?
|
||||
},
|
||||
"metadata": {}
|
||||
}))
|
||||
})))
|
||||
}
|
||||
|
||||
#[get("/about")]
|
||||
pub fn about(user: Option<User>, conn: DbConn, intl: I18n) -> Ructe {
|
||||
render!(instance::about(
|
||||
pub fn about(user: Option<User>, conn: DbConn, intl: I18n) -> Result<Ructe, ErrorPage> {
|
||||
Ok(render!(instance::about(
|
||||
&(&*conn, &intl.catalog, user),
|
||||
Instance::get_local(&*conn).expect("Local instance not found"),
|
||||
Instance::get_local(&*conn).expect("Local instance not found").main_admin(&*conn),
|
||||
User::count_local(&*conn),
|
||||
Post::count_local(&*conn),
|
||||
Instance::count(&*conn) - 1
|
||||
))
|
||||
Instance::get_local(&*conn)?,
|
||||
Instance::get_local(&*conn)?.main_admin(&*conn)?,
|
||||
User::count_local(&*conn)?,
|
||||
Post::count_local(&*conn)?,
|
||||
Instance::count(&*conn)? - 1
|
||||
)))
|
||||
}
|
||||
|
||||
#[get("/manifest.json")]
|
||||
pub fn web_manifest(conn: DbConn) -> Json<serde_json::Value> {
|
||||
let instance = Instance::get_local(&*conn).expect("instance::web_manifest: local instance not found error");
|
||||
Json(json!({
|
||||
pub fn web_manifest(conn: DbConn) -> Result<Json<serde_json::Value>, ErrorPage> {
|
||||
let instance = Instance::get_local(&*conn)?;
|
||||
Ok(Json(json!({
|
||||
"name": &instance.name,
|
||||
"description": &instance.short_description,
|
||||
"start_url": String::from("/"),
|
||||
@@ -306,5 +298,5 @@ pub fn web_manifest(conn: DbConn) -> Json<serde_json::Value> {
|
||||
"src": "/static/icons/trwnh/feather/plumeFeather.svg"
|
||||
}
|
||||
]
|
||||
}))
|
||||
})))
|
||||
}
|
||||
|
||||
+11
-10
@@ -11,27 +11,28 @@ use plume_models::{
|
||||
users::User
|
||||
};
|
||||
use Worker;
|
||||
use routes::errors::ErrorPage;
|
||||
|
||||
#[post("/~/<blog>/<slug>/like")]
|
||||
pub fn create(blog: String, slug: String, user: User, conn: DbConn, worker: Worker) -> Option<Redirect> {
|
||||
pub fn create(blog: String, slug: String, user: User, conn: DbConn, worker: Worker) -> Result<Redirect, ErrorPage> {
|
||||
let b = Blog::find_by_fqn(&*conn, &blog)?;
|
||||
let post = Post::find_by_slug(&*conn, &slug, b.id)?;
|
||||
|
||||
if !user.has_liked(&*conn, &post) {
|
||||
let like = likes::Like::insert(&*conn, likes::NewLike::new(&post ,&user));
|
||||
like.notify(&*conn);
|
||||
if !user.has_liked(&*conn, &post)? {
|
||||
let like = likes::Like::insert(&*conn, likes::NewLike::new(&post ,&user))?;
|
||||
like.notify(&*conn)?;
|
||||
|
||||
let dest = User::one_by_instance(&*conn);
|
||||
let act = like.to_activity(&*conn);
|
||||
let dest = User::one_by_instance(&*conn)?;
|
||||
let act = like.to_activity(&*conn)?;
|
||||
worker.execute(move || broadcast(&user, act, dest));
|
||||
} else {
|
||||
let like = likes::Like::find_by_user_on_post(&*conn, user.id, post.id).expect("likes::create: like exist but not found error");
|
||||
let delete_act = like.delete(&*conn);
|
||||
let dest = User::one_by_instance(&*conn);
|
||||
let like = likes::Like::find_by_user_on_post(&*conn, user.id, post.id)?;
|
||||
let delete_act = like.delete(&*conn)?;
|
||||
let dest = User::one_by_instance(&*conn)?;
|
||||
worker.execute(move || broadcast(&user, delete_act, dest));
|
||||
}
|
||||
|
||||
Some(Redirect::to(uri!(super::posts::details: blog = blog, slug = slug, responding_to = _)))
|
||||
Ok(Redirect::to(uri!(super::posts::details: blog = blog, slug = slug, responding_to = _)))
|
||||
}
|
||||
|
||||
#[post("/~/<blog>/<slug>/like", rank = 2)]
|
||||
|
||||
+24
-27
@@ -5,14 +5,15 @@ use rocket_i18n::I18n;
|
||||
use std::fs;
|
||||
use plume_models::{db_conn::DbConn, medias::*, users::User};
|
||||
use template_utils::Ructe;
|
||||
use routes::errors::ErrorPage;
|
||||
|
||||
#[get("/medias")]
|
||||
pub fn list(user: User, conn: DbConn, intl: I18n) -> Ructe {
|
||||
let medias = Media::for_user(&*conn, user.id);
|
||||
render!(medias::index(
|
||||
pub fn list(user: User, conn: DbConn, intl: I18n) -> Result<Ructe, ErrorPage> {
|
||||
let medias = Media::for_user(&*conn, user.id)?;
|
||||
Ok(render!(medias::index(
|
||||
&(&*conn, &intl.catalog, Some(user)),
|
||||
medias
|
||||
))
|
||||
)))
|
||||
}
|
||||
|
||||
#[get("/medias/new")]
|
||||
@@ -39,69 +40,65 @@ pub fn upload(user: User, data: Data, ct: &ContentType, conn: DbConn) -> Result<
|
||||
let dest = format!("static/media/{}.{}", GUID::rand().to_string(), ext);
|
||||
|
||||
match fields[&"file".to_string()][0].data {
|
||||
SavedData::Bytes(ref bytes) => fs::write(&dest, bytes).expect("media::upload: Couldn't save upload"),
|
||||
SavedData::File(ref path, _) => {fs::copy(path, &dest).expect("media::upload: Couldn't copy upload");},
|
||||
SavedData::Bytes(ref bytes) => fs::write(&dest, bytes).map_err(|_| status::BadRequest(Some("Couldn't save upload")))?,
|
||||
SavedData::File(ref path, _) => {fs::copy(path, &dest).map_err(|_| status::BadRequest(Some("Couldn't copy upload")))?;},
|
||||
_ => {
|
||||
println!("not a file");
|
||||
return Ok(Redirect::to(uri!(new)));
|
||||
}
|
||||
}
|
||||
|
||||
let has_cw = !read(&fields[&"cw".to_string()][0].data).is_empty();
|
||||
let has_cw = !read(&fields[&"cw".to_string()][0].data).map(|cw| cw.is_empty()).unwrap_or(false);
|
||||
let media = Media::insert(&*conn, NewMedia {
|
||||
file_path: dest,
|
||||
alt_text: read(&fields[&"alt".to_string()][0].data),
|
||||
alt_text: read(&fields[&"alt".to_string()][0].data)?,
|
||||
is_remote: false,
|
||||
remote_url: None,
|
||||
sensitive: has_cw,
|
||||
content_warning: if has_cw {
|
||||
Some(read(&fields[&"cw".to_string()][0].data))
|
||||
Some(read(&fields[&"cw".to_string()][0].data)?)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
owner_id: user.id
|
||||
});
|
||||
println!("ok");
|
||||
}).map_err(|_| status::BadRequest(Some("Error while saving media")))?;
|
||||
Ok(Redirect::to(uri!(details: id = media.id)))
|
||||
},
|
||||
SaveResult::Partial(_, _) | SaveResult::Error(_) => {
|
||||
println!("partial err");
|
||||
Ok(Redirect::to(uri!(new)))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("not form data");
|
||||
Ok(Redirect::to(uri!(new)))
|
||||
}
|
||||
}
|
||||
|
||||
fn read(data: &SavedData) -> String {
|
||||
fn read(data: &SavedData) -> Result<String, status::BadRequest<&'static str>> {
|
||||
if let SavedData::Text(s) = data {
|
||||
s.clone()
|
||||
Ok(s.clone())
|
||||
} else {
|
||||
panic!("Field is not a string")
|
||||
Err(status::BadRequest(Some("Error while reading data")))
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/medias/<id>")]
|
||||
pub fn details(id: i32, user: User, conn: DbConn, intl: I18n) -> Ructe {
|
||||
let media = Media::get(&*conn, id).expect("Media::details: media not found");
|
||||
render!(medias::details(
|
||||
pub fn details(id: i32, user: User, conn: DbConn, intl: I18n) -> Result<Ructe, ErrorPage> {
|
||||
let media = Media::get(&*conn, id)?;
|
||||
Ok(render!(medias::details(
|
||||
&(&*conn, &intl.catalog, Some(user)),
|
||||
media
|
||||
))
|
||||
)))
|
||||
}
|
||||
|
||||
#[post("/medias/<id>/delete")]
|
||||
pub fn delete(id: i32, _user: User, conn: DbConn) -> Option<Redirect> {
|
||||
pub fn delete(id: i32, _user: User, conn: DbConn) -> Result<Redirect, ErrorPage> {
|
||||
let media = Media::get(&*conn, id)?;
|
||||
media.delete(&*conn);
|
||||
Some(Redirect::to(uri!(list)))
|
||||
media.delete(&*conn)?;
|
||||
Ok(Redirect::to(uri!(list)))
|
||||
}
|
||||
|
||||
#[post("/medias/<id>/avatar")]
|
||||
pub fn set_avatar(id: i32, user: User, conn: DbConn) -> Option<Redirect> {
|
||||
pub fn set_avatar(id: i32, user: User, conn: DbConn) -> Result<Redirect, ErrorPage> {
|
||||
let media = Media::get(&*conn, id)?;
|
||||
user.set_avatar(&*conn, media.id);
|
||||
Some(Redirect::to(uri!(details: id = id)))
|
||||
user.set_avatar(&*conn, media.id)?;
|
||||
Ok(Redirect::to(uri!(details: id = id)))
|
||||
}
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ pub fn post_to_atom(post: Post, conn: &Connection) -> Entry {
|
||||
.src(post.ap_url.clone())
|
||||
.content_type("html".to_string())
|
||||
.build().expect("Atom feed: content error"))
|
||||
.authors(post.get_authors(&*conn)
|
||||
.authors(post.get_authors(&*conn).expect("Atom feed: author error")
|
||||
.into_iter()
|
||||
.map(|a| PersonBuilder::default()
|
||||
.name(a.display_name)
|
||||
|
||||
@@ -3,18 +3,18 @@ use rocket_i18n::I18n;
|
||||
|
||||
use plume_common::utils;
|
||||
use plume_models::{db_conn::DbConn, notifications::Notification, users::User};
|
||||
use routes::Page;
|
||||
use routes::{Page, errors::ErrorPage};
|
||||
use template_utils::Ructe;
|
||||
|
||||
#[get("/notifications?<page>")]
|
||||
pub fn notifications(conn: DbConn, user: User, page: Option<Page>, intl: I18n) -> Ructe {
|
||||
pub fn notifications(conn: DbConn, user: User, page: Option<Page>, intl: I18n) -> Result<Ructe, ErrorPage> {
|
||||
let page = page.unwrap_or_default();
|
||||
render!(notifications::index(
|
||||
Ok(render!(notifications::index(
|
||||
&(&*conn, &intl.catalog, Some(user.clone())),
|
||||
Notification::page_for_user(&*conn, &user, page.limits()),
|
||||
Notification::page_for_user(&*conn, &user, page.limits())?,
|
||||
page.0,
|
||||
Page::total(Notification::count_for_user(&*conn, &user) as i32)
|
||||
))
|
||||
Page::total(Notification::count_for_user(&*conn, &user)? as i32)
|
||||
)))
|
||||
}
|
||||
|
||||
#[get("/notifications?<page>", rank = 2)]
|
||||
|
||||
+87
-79
@@ -21,20 +21,19 @@ use plume_models::{
|
||||
tags::*,
|
||||
users::User
|
||||
};
|
||||
use routes::comments::NewCommentForm;
|
||||
use routes::{errors::ErrorPage, comments::NewCommentForm};
|
||||
use template_utils::Ructe;
|
||||
use Worker;
|
||||
use Searcher;
|
||||
|
||||
#[get("/~/<blog>/<slug>?<responding_to>", rank = 4)]
|
||||
pub fn details(blog: String, slug: String, conn: DbConn, user: Option<User>, responding_to: Option<i32>, intl: I18n) -> Result<Ructe, Ructe> {
|
||||
let blog = Blog::find_by_fqn(&*conn, &blog).ok_or_else(|| render!(errors::not_found(&(&*conn, &intl.catalog, user.clone()))))?;
|
||||
let post = Post::find_by_slug(&*conn, &slug, blog.id).ok_or_else(|| render!(errors::not_found(&(&*conn, &intl.catalog, user.clone()))))?;
|
||||
if post.published || post.get_authors(&*conn).into_iter().any(|a| a.id == user.clone().map(|u| u.id).unwrap_or(0)) {
|
||||
let comments = CommentTree::from_post(&*conn, &post, user.as_ref());
|
||||
pub fn details(blog: String, slug: String, conn: DbConn, user: Option<User>, responding_to: Option<i32>, intl: I18n) -> Result<Ructe, ErrorPage> {
|
||||
let blog = Blog::find_by_fqn(&*conn, &blog)?;
|
||||
let post = Post::find_by_slug(&*conn, &slug, blog.id)?;
|
||||
if post.published || post.get_authors(&*conn)?.into_iter().any(|a| a.id == user.clone().map(|u| u.id).unwrap_or(0)) {
|
||||
let comments = CommentTree::from_post(&*conn, &post, user.as_ref())?;
|
||||
|
||||
let previous = responding_to.map(|r| Comment::get(&*conn, r)
|
||||
.expect("posts::details_reponse: Error retrieving previous comment"));
|
||||
let previous = responding_to.and_then(|r| Comment::get(&*conn, r).ok());
|
||||
|
||||
Ok(render!(posts::details(
|
||||
&(&*conn, &intl.catalog, user.clone()),
|
||||
@@ -42,14 +41,14 @@ pub fn details(blog: String, slug: String, conn: DbConn, user: Option<User>, res
|
||||
blog,
|
||||
&NewCommentForm {
|
||||
warning: previous.clone().map(|p| p.spoiler_text).unwrap_or_default(),
|
||||
content: previous.clone().map(|p| format!(
|
||||
content: previous.clone().and_then(|p| Some(format!(
|
||||
"@{} {}",
|
||||
p.get_author(&*conn).get_fqn(&*conn),
|
||||
Mention::list_for_comment(&*conn, p.id)
|
||||
p.get_author(&*conn).ok()?.get_fqn(&*conn),
|
||||
Mention::list_for_comment(&*conn, p.id).ok()?
|
||||
.into_iter()
|
||||
.filter_map(|m| {
|
||||
let user = user.clone();
|
||||
if let Some(mentioned) = m.get_mentioned(&*conn) {
|
||||
if let Ok(mentioned) = m.get_mentioned(&*conn) {
|
||||
if user.is_none() || mentioned.id != user.expect("posts::details_response: user error while listing mentions").id {
|
||||
Some(format!("@{}", mentioned.get_fqn(&*conn)))
|
||||
} else {
|
||||
@@ -59,22 +58,22 @@ pub fn details(blog: String, slug: String, conn: DbConn, user: Option<User>, res
|
||||
None
|
||||
}
|
||||
}).collect::<Vec<String>>().join(" "))
|
||||
).unwrap_or_default(),
|
||||
)).unwrap_or_default(),
|
||||
..NewCommentForm::default()
|
||||
},
|
||||
ValidationErrors::default(),
|
||||
Tag::for_post(&*conn, post.id),
|
||||
Tag::for_post(&*conn, post.id)?,
|
||||
comments,
|
||||
previous,
|
||||
post.count_likes(&*conn),
|
||||
post.count_reshares(&*conn),
|
||||
user.clone().map(|u| u.has_liked(&*conn, &post)).unwrap_or(false),
|
||||
user.clone().map(|u| u.has_reshared(&*conn, &post)).unwrap_or(false),
|
||||
user.map(|u| u.is_following(&*conn, post.get_authors(&*conn)[0].id)).unwrap_or(false),
|
||||
post.get_authors(&*conn)[0].clone()
|
||||
post.count_likes(&*conn)?,
|
||||
post.count_reshares(&*conn)?,
|
||||
user.clone().and_then(|u| u.has_liked(&*conn, &post).ok()).unwrap_or(false),
|
||||
user.clone().and_then(|u| u.has_reshared(&*conn, &post).ok()).unwrap_or(false),
|
||||
user.and_then(|u| u.is_following(&*conn, post.get_authors(&*conn).ok()?[0].id).ok()).unwrap_or(false),
|
||||
post.get_authors(&*conn)?[0].clone()
|
||||
)))
|
||||
} else {
|
||||
Err(render!(errors::not_authorized(
|
||||
Ok(render!(errors::not_authorized(
|
||||
&(&*conn, &intl.catalog, user.clone()),
|
||||
"This post isn't published yet."
|
||||
)))
|
||||
@@ -83,10 +82,10 @@ pub fn details(blog: String, slug: String, conn: DbConn, user: Option<User>, res
|
||||
|
||||
#[get("/~/<blog>/<slug>", rank = 3)]
|
||||
pub fn activity_details(blog: String, slug: String, conn: DbConn, _ap: ApRequest) -> Result<ActivityStream<LicensedArticle>, Option<String>> {
|
||||
let blog = Blog::find_by_fqn(&*conn, &blog).ok_or(None)?;
|
||||
let post = Post::find_by_slug(&*conn, &slug, blog.id).ok_or(None)?;
|
||||
let blog = Blog::find_by_fqn(&*conn, &blog).map_err(|_| None)?;
|
||||
let post = Post::find_by_slug(&*conn, &slug, blog.id).map_err(|_| None)?;
|
||||
if post.published {
|
||||
Ok(ActivityStream::new(post.to_activity(&*conn)))
|
||||
Ok(ActivityStream::new(post.to_activity(&*conn).map_err(|_| String::from("Post serialization error"))?))
|
||||
} else {
|
||||
Err(Some(String::from("Not published yet.")))
|
||||
}
|
||||
@@ -101,23 +100,23 @@ pub fn new_auth(blog: String, i18n: I18n) -> Flash<Redirect> {
|
||||
}
|
||||
|
||||
#[get("/~/<blog>/new", rank = 1)]
|
||||
pub fn new(blog: String, user: User, conn: DbConn, intl: I18n) -> Option<Ructe> {
|
||||
pub fn new(blog: String, user: User, conn: DbConn, intl: I18n) -> Result<Ructe, ErrorPage> {
|
||||
let b = Blog::find_by_fqn(&*conn, &blog)?;
|
||||
|
||||
if !user.is_author_in(&*conn, &b) {
|
||||
if !user.is_author_in(&*conn, &b)? {
|
||||
// TODO actually return 403 error code
|
||||
Some(render!(errors::not_authorized(
|
||||
Ok(render!(errors::not_authorized(
|
||||
&(&*conn, &intl.catalog, Some(user)),
|
||||
"You are not author in this blog."
|
||||
)))
|
||||
} else {
|
||||
let medias = Media::for_user(&*conn, user.id);
|
||||
Some(render!(posts::new(
|
||||
let medias = Media::for_user(&*conn, user.id)?;
|
||||
Ok(render!(posts::new(
|
||||
&(&*conn, &intl.catalog, Some(user)),
|
||||
b,
|
||||
false,
|
||||
&NewPostForm {
|
||||
license: Instance::get_local(&*conn).map(|i| i.default_license).unwrap_or_else(||String::from("CC-BY-SA")),
|
||||
license: Instance::get_local(&*conn)?.default_license,
|
||||
..NewPostForm::default()
|
||||
},
|
||||
true,
|
||||
@@ -129,12 +128,12 @@ pub fn new(blog: String, user: User, conn: DbConn, intl: I18n) -> Option<Ructe>
|
||||
}
|
||||
|
||||
#[get("/~/<blog>/<slug>/edit")]
|
||||
pub fn edit(blog: String, slug: String, user: User, conn: DbConn, intl: I18n) -> Option<Ructe> {
|
||||
pub fn edit(blog: String, slug: String, user: User, conn: DbConn, intl: I18n) -> Result<Ructe, ErrorPage> {
|
||||
let b = Blog::find_by_fqn(&*conn, &blog)?;
|
||||
let post = Post::find_by_slug(&*conn, &slug, b.id)?;
|
||||
|
||||
if !user.is_author_in(&*conn, &b) {
|
||||
Some(render!(errors::not_authorized(
|
||||
if !user.is_author_in(&*conn, &b)? {
|
||||
Ok(render!(errors::not_authorized(
|
||||
&(&*conn, &intl.catalog, Some(user)),
|
||||
"You are not author in this blog."
|
||||
)))
|
||||
@@ -145,8 +144,8 @@ pub fn edit(blog: String, slug: String, user: User, conn: DbConn, intl: I18n) ->
|
||||
post.content.get().clone() // fallback to HTML if the markdown was not stored
|
||||
};
|
||||
|
||||
let medias = Media::for_user(&*conn, user.id);
|
||||
Some(render!(posts::new(
|
||||
let medias = Media::for_user(&*conn, user.id)?;
|
||||
Ok(render!(posts::new(
|
||||
&(&*conn, &intl.catalog, Some(user)),
|
||||
b,
|
||||
true,
|
||||
@@ -154,7 +153,7 @@ pub fn edit(blog: String, slug: String, user: User, conn: DbConn, intl: I18n) ->
|
||||
title: post.title.clone(),
|
||||
subtitle: post.subtitle.clone(),
|
||||
content: source,
|
||||
tags: Tag::for_post(&*conn, post.id)
|
||||
tags: Tag::for_post(&*conn, post.id)?
|
||||
.into_iter()
|
||||
.filter_map(|t| if !t.is_hashtag {Some(t.tag)} else {None})
|
||||
.collect::<Vec<String>>()
|
||||
@@ -173,9 +172,9 @@ pub fn edit(blog: String, slug: String, user: User, conn: DbConn, intl: I18n) ->
|
||||
|
||||
#[post("/~/<blog>/<slug>/edit", data = "<form>")]
|
||||
pub fn update(blog: String, slug: String, user: User, conn: DbConn, form: LenientForm<NewPostForm>, worker: Worker, intl: I18n, searcher: Searcher)
|
||||
-> Result<Redirect, Option<Ructe>> {
|
||||
let b = Blog::find_by_fqn(&*conn, &blog).ok_or(None)?;
|
||||
let mut post = Post::find_by_slug(&*conn, &slug, b.id).ok_or(None)?;
|
||||
-> Result<Redirect, Ructe> {
|
||||
let b = Blog::find_by_fqn(&*conn, &blog).expect("post::update: blog error");
|
||||
let mut post = Post::find_by_slug(&*conn, &slug, b.id).expect("post::update: find by slug error");
|
||||
|
||||
let new_slug = if !post.published {
|
||||
form.title.to_string().to_kebab_case()
|
||||
@@ -188,7 +187,7 @@ pub fn update(blog: String, slug: String, user: User, conn: DbConn, form: Lenien
|
||||
Err(e) => e
|
||||
};
|
||||
|
||||
if new_slug != slug && Post::find_by_slug(&*conn, &new_slug, b.id).is_some() {
|
||||
if new_slug != slug && Post::find_by_slug(&*conn, &new_slug, b.id).is_ok() {
|
||||
errors.add("title", ValidationError {
|
||||
code: Cow::from("existing_slug"),
|
||||
message: Some(Cow::from("A post with the same title already exists.")),
|
||||
@@ -197,7 +196,7 @@ pub fn update(blog: String, slug: String, user: User, conn: DbConn, form: Lenien
|
||||
}
|
||||
|
||||
if errors.is_empty() {
|
||||
if !user.is_author_in(&*conn, &b) {
|
||||
if !user.is_author_in(&*conn, &b).expect("posts::update: is author in error") {
|
||||
// actually it's not "Ok"…
|
||||
Ok(Redirect::to(uri!(super::blogs::details: name = blog, page = _)))
|
||||
} else {
|
||||
@@ -219,29 +218,30 @@ pub fn update(blog: String, slug: String, user: User, conn: DbConn, form: Lenien
|
||||
post.source = form.content.clone();
|
||||
post.license = form.license.clone();
|
||||
post.cover_id = form.cover;
|
||||
post.update(&*conn, &searcher);
|
||||
let post = post.update_ap_url(&*conn);
|
||||
post.update(&*conn, &searcher).expect("post::update: update error");;
|
||||
let post = post.update_ap_url(&*conn).expect("post::update: update ap url error");
|
||||
|
||||
if post.published {
|
||||
post.update_mentions(&conn, mentions.into_iter().map(|m| Mention::build_activity(&conn, &m)).collect());
|
||||
post.update_mentions(&conn, mentions.into_iter().filter_map(|m| Mention::build_activity(&conn, &m).ok()).collect())
|
||||
.expect("post::update: mentions error");;
|
||||
}
|
||||
|
||||
let tags = form.tags.split(',').map(|t| t.trim().to_camel_case()).filter(|t| !t.is_empty())
|
||||
.collect::<HashSet<_>>().into_iter().map(|t| Tag::build_activity(&conn, t)).collect::<Vec<_>>();
|
||||
post.update_tags(&conn, tags);
|
||||
.collect::<HashSet<_>>().into_iter().filter_map(|t| Tag::build_activity(&conn, t).ok()).collect::<Vec<_>>();
|
||||
post.update_tags(&conn, tags).expect("post::update: tags error");
|
||||
|
||||
let hashtags = hashtags.into_iter().map(|h| h.to_camel_case()).collect::<HashSet<_>>()
|
||||
.into_iter().map(|t| Tag::build_activity(&conn, t)).collect::<Vec<_>>();
|
||||
post.update_hashtags(&conn, hashtags);
|
||||
.into_iter().filter_map(|t| Tag::build_activity(&conn, t).ok()).collect::<Vec<_>>();
|
||||
post.update_hashtags(&conn, hashtags).expect("post::update: hashtags error");
|
||||
|
||||
if post.published {
|
||||
if newly_published {
|
||||
let act = post.create_activity(&conn);
|
||||
let dest = User::one_by_instance(&*conn);
|
||||
let act = post.create_activity(&conn).expect("post::update: act error");
|
||||
let dest = User::one_by_instance(&*conn).expect("post::update: dest error");
|
||||
worker.execute(move || broadcast(&user, act, dest));
|
||||
} else {
|
||||
let act = post.update_activity(&*conn);
|
||||
let dest = User::one_by_instance(&*conn);
|
||||
let act = post.update_activity(&*conn).expect("post::update: act error");
|
||||
let dest = User::one_by_instance(&*conn).expect("posts::update: dest error");
|
||||
worker.execute(move || broadcast(&user, act, dest));
|
||||
}
|
||||
}
|
||||
@@ -249,8 +249,8 @@ pub fn update(blog: String, slug: String, user: User, conn: DbConn, form: Lenien
|
||||
Ok(Redirect::to(uri!(details: blog = blog, slug = new_slug, responding_to = _)))
|
||||
}
|
||||
} else {
|
||||
let medias = Media::for_user(&*conn, user.id);
|
||||
let temp = render!(posts::new(
|
||||
let medias = Media::for_user(&*conn, user.id).expect("posts:update: medias error");
|
||||
Err(render!(posts::new(
|
||||
&(&*conn, &intl.catalog, Some(user)),
|
||||
b,
|
||||
true,
|
||||
@@ -259,8 +259,7 @@ pub fn update(blog: String, slug: String, user: User, conn: DbConn, form: Lenien
|
||||
Some(post),
|
||||
errors.clone(),
|
||||
medias.clone()
|
||||
));
|
||||
Err(Some(temp))
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,15 +287,15 @@ pub fn valid_slug(title: &str) -> Result<(), ValidationError> {
|
||||
}
|
||||
|
||||
#[post("/~/<blog_name>/new", data = "<form>")]
|
||||
pub fn create(blog_name: String, form: LenientForm<NewPostForm>, user: User, conn: DbConn, worker: Worker, intl: I18n, searcher: Searcher) -> Result<Redirect, Option<Ructe>> {
|
||||
let blog = Blog::find_by_fqn(&*conn, &blog_name).ok_or(None)?;
|
||||
pub fn create(blog_name: String, form: LenientForm<NewPostForm>, user: User, conn: DbConn, worker: Worker, intl: I18n, searcher: Searcher) -> Result<Redirect, Result<Ructe, ErrorPage>> {
|
||||
let blog = Blog::find_by_fqn(&*conn, &blog_name).expect("post::create: blog error");;
|
||||
let slug = form.title.to_string().to_kebab_case();
|
||||
|
||||
let mut errors = match form.validate() {
|
||||
Ok(_) => ValidationErrors::new(),
|
||||
Err(e) => e
|
||||
};
|
||||
if Post::find_by_slug(&*conn, &slug, blog.id).is_some() {
|
||||
if Post::find_by_slug(&*conn, &slug, blog.id).is_ok() {
|
||||
errors.add("title", ValidationError {
|
||||
code: Cow::from("existing_slug"),
|
||||
message: Some(Cow::from("A post with the same title already exists.")),
|
||||
@@ -305,11 +304,14 @@ pub fn create(blog_name: String, form: LenientForm<NewPostForm>, user: User, con
|
||||
}
|
||||
|
||||
if errors.is_empty() {
|
||||
if !user.is_author_in(&*conn, &blog) {
|
||||
if !user.is_author_in(&*conn, &blog).expect("post::create: is author in error") {
|
||||
// actually it's not "Ok"…
|
||||
Ok(Redirect::to(uri!(super::blogs::details: name = blog_name, page = _)))
|
||||
} else {
|
||||
let (content, mentions, hashtags) = utils::md_to_html(form.content.to_string().as_ref(), &Instance::get_local(&conn).expect("posts::create: Error getting l ocal instance").public_domain);
|
||||
let (content, mentions, hashtags) = utils::md_to_html(
|
||||
form.content.to_string().as_ref(),
|
||||
&Instance::get_local(&conn).expect("post::create: local instance error").public_domain
|
||||
);
|
||||
|
||||
let post = Post::insert(&*conn, NewPost {
|
||||
blog_id: blog.id,
|
||||
@@ -325,12 +327,12 @@ pub fn create(blog_name: String, form: LenientForm<NewPostForm>, user: User, con
|
||||
cover_id: form.cover,
|
||||
},
|
||||
&searcher,
|
||||
);
|
||||
let post = post.update_ap_url(&*conn);
|
||||
).expect("post::create: post save error");
|
||||
let post = post.update_ap_url(&*conn).expect("post::create: update ap url error");
|
||||
PostAuthor::insert(&*conn, NewPostAuthor {
|
||||
post_id: post.id,
|
||||
author_id: user.id
|
||||
});
|
||||
}).expect("post::create: author save error");
|
||||
|
||||
let tags = form.tags.split(',')
|
||||
.map(|t| t.trim().to_camel_case())
|
||||
@@ -341,31 +343,37 @@ pub fn create(blog_name: String, form: LenientForm<NewPostForm>, user: User, con
|
||||
tag,
|
||||
is_hashtag: false,
|
||||
post_id: post.id
|
||||
});
|
||||
}).expect("post::create: tags save error");
|
||||
}
|
||||
for hashtag in hashtags {
|
||||
Tag::insert(&*conn, NewTag {
|
||||
tag: hashtag.to_camel_case(),
|
||||
is_hashtag: true,
|
||||
post_id: post.id
|
||||
});
|
||||
}).expect("post::create: hashtags save error");
|
||||
}
|
||||
|
||||
if post.published {
|
||||
for m in mentions {
|
||||
Mention::from_activity(&*conn, &Mention::build_activity(&*conn, &m), post.id, true, true);
|
||||
Mention::from_activity(
|
||||
&*conn,
|
||||
&Mention::build_activity(&*conn, &m).expect("post::create: mention build error"),
|
||||
post.id,
|
||||
true,
|
||||
true
|
||||
).expect("post::create: mention save error");
|
||||
}
|
||||
|
||||
let act = post.create_activity(&*conn);
|
||||
let dest = User::one_by_instance(&*conn);
|
||||
let act = post.create_activity(&*conn).expect("posts::create: activity error");
|
||||
let dest = User::one_by_instance(&*conn).expect("posts::create: dest error");
|
||||
worker.execute(move || broadcast(&user, act, dest));
|
||||
}
|
||||
|
||||
Ok(Redirect::to(uri!(details: blog = blog_name, slug = slug, responding_to = _)))
|
||||
}
|
||||
} else {
|
||||
let medias = Media::for_user(&*conn, user.id);
|
||||
Err(Some(render!(posts::new(
|
||||
let medias = Media::for_user(&*conn, user.id).expect("posts::create: medias error");
|
||||
Err(Ok(render!(posts::new(
|
||||
&(&*conn, &intl.catalog, Some(user)),
|
||||
blog,
|
||||
false,
|
||||
@@ -379,21 +387,21 @@ pub fn create(blog_name: String, form: LenientForm<NewPostForm>, user: User, con
|
||||
}
|
||||
|
||||
#[post("/~/<blog_name>/<slug>/delete")]
|
||||
pub fn delete(blog_name: String, slug: String, conn: DbConn, user: User, worker: Worker, searcher: Searcher) -> Redirect {
|
||||
pub fn delete(blog_name: String, slug: String, conn: DbConn, user: User, worker: Worker, searcher: Searcher) -> Result<Redirect, ErrorPage> {
|
||||
let post = Blog::find_by_fqn(&*conn, &blog_name)
|
||||
.and_then(|blog| Post::find_by_slug(&*conn, &slug, blog.id));
|
||||
|
||||
if let Some(post) = post {
|
||||
if !post.get_authors(&*conn).into_iter().any(|a| a.id == user.id) {
|
||||
Redirect::to(uri!(details: blog = blog_name.clone(), slug = slug.clone(), responding_to = _))
|
||||
if let Ok(post) = post {
|
||||
if !post.get_authors(&*conn)?.into_iter().any(|a| a.id == user.id) {
|
||||
Ok(Redirect::to(uri!(details: blog = blog_name.clone(), slug = slug.clone(), responding_to = _)))
|
||||
} else {
|
||||
let dest = User::one_by_instance(&*conn);
|
||||
let delete_activity = post.delete(&(&conn, &searcher));
|
||||
let dest = User::one_by_instance(&*conn)?;
|
||||
let delete_activity = post.delete(&(&conn, &searcher))?;
|
||||
worker.execute(move || broadcast(&user, delete_activity, dest));
|
||||
|
||||
Redirect::to(uri!(super::blogs::details: name = blog_name, page = _))
|
||||
Ok(Redirect::to(uri!(super::blogs::details: name = blog_name, page = _)))
|
||||
}
|
||||
} else {
|
||||
Redirect::to(uri!(super::blogs::details: name = blog_name, page = _))
|
||||
Ok(Redirect::to(uri!(super::blogs::details: name = blog_name, page = _)))
|
||||
}
|
||||
}
|
||||
|
||||
+11
-11
@@ -10,29 +10,29 @@ use plume_models::{
|
||||
reshares::*,
|
||||
users::User
|
||||
};
|
||||
use routes::errors::ErrorPage;
|
||||
use Worker;
|
||||
|
||||
#[post("/~/<blog>/<slug>/reshare")]
|
||||
pub fn create(blog: String, slug: String, user: User, conn: DbConn, worker: Worker) -> Option<Redirect> {
|
||||
pub fn create(blog: String, slug: String, user: User, conn: DbConn, worker: Worker) -> Result<Redirect, ErrorPage> {
|
||||
let b = Blog::find_by_fqn(&*conn, &blog)?;
|
||||
let post = Post::find_by_slug(&*conn, &slug, b.id)?;
|
||||
|
||||
if !user.has_reshared(&*conn, &post) {
|
||||
let reshare = Reshare::insert(&*conn, NewReshare::new(&post, &user));
|
||||
reshare.notify(&*conn);
|
||||
if !user.has_reshared(&*conn, &post)? {
|
||||
let reshare = Reshare::insert(&*conn, NewReshare::new(&post, &user))?;
|
||||
reshare.notify(&*conn)?;
|
||||
|
||||
let dest = User::one_by_instance(&*conn);
|
||||
let act = reshare.to_activity(&*conn);
|
||||
let dest = User::one_by_instance(&*conn)?;
|
||||
let act = reshare.to_activity(&*conn)?;
|
||||
worker.execute(move || broadcast(&user, act, dest));
|
||||
} else {
|
||||
let reshare = Reshare::find_by_user_on_post(&*conn, user.id, post.id)
|
||||
.expect("reshares::create: reshare exist but not found error");
|
||||
let delete_act = reshare.delete(&*conn);
|
||||
let dest = User::one_by_instance(&*conn);
|
||||
let reshare = Reshare::find_by_user_on_post(&*conn, user.id, post.id)?;
|
||||
let delete_act = reshare.delete(&*conn)?;
|
||||
let dest = User::one_by_instance(&*conn)?;
|
||||
worker.execute(move || broadcast(&user, delete_act, dest));
|
||||
}
|
||||
|
||||
Some(Redirect::to(uri!(super::posts::details: blog = blog, slug = slug, responding_to = _)))
|
||||
Ok(Redirect::to(uri!(super::posts::details: blog = blog, slug = slug, responding_to = _)))
|
||||
}
|
||||
|
||||
#[post("/~/<blog>/<slug>/reshare", rank=1)]
|
||||
|
||||
+12
-7
@@ -14,6 +14,7 @@ use plume_models::{
|
||||
users::{User, AUTH_COOKIE}
|
||||
};
|
||||
|
||||
|
||||
#[get("/login?<m>")]
|
||||
pub fn new(user: Option<User>, conn: DbConn, m: Option<String>, intl: I18n) -> Ructe {
|
||||
render!(session::login(
|
||||
@@ -35,30 +36,34 @@ pub struct LoginForm {
|
||||
#[post("/login", data = "<form>")]
|
||||
pub fn create(conn: DbConn, form: LenientForm<LoginForm>, flash: Option<FlashMessage>, mut cookies: Cookies, intl: I18n) -> Result<Redirect, Ructe> {
|
||||
let user = User::find_by_email(&*conn, &form.email_or_name)
|
||||
.or_else(|| User::find_local(&*conn, &form.email_or_name));
|
||||
.or_else(|_| User::find_local(&*conn, &form.email_or_name));
|
||||
let mut errors = match form.validate() {
|
||||
Ok(_) => ValidationErrors::new(),
|
||||
Err(e) => e
|
||||
};
|
||||
|
||||
if let Some(user) = user.clone() {
|
||||
let user_id = if let Ok(user) = user {
|
||||
if !user.auth(&form.password) {
|
||||
let mut err = ValidationError::new("invalid_login");
|
||||
err.message = Some(Cow::from("Invalid username or password"));
|
||||
errors.add("email_or_name", err)
|
||||
errors.add("email_or_name", err);
|
||||
user.id.to_string()
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
} else {
|
||||
// Fake password verification, only to avoid different login times
|
||||
// that could be used to see if an email adress is registered or not
|
||||
User::get(&*conn, 1).map(|u| u.auth(&form.password));
|
||||
User::get(&*conn, 1).map(|u| u.auth(&form.password)).expect("No user is registered");
|
||||
|
||||
let mut err = ValidationError::new("invalid_login");
|
||||
err.message = Some(Cow::from("Invalid username or password"));
|
||||
errors.add("email_or_name", err)
|
||||
}
|
||||
errors.add("email_or_name", err);
|
||||
String::new()
|
||||
};
|
||||
|
||||
if errors.is_empty() {
|
||||
cookies.add_private(Cookie::build(AUTH_COOKIE, user.unwrap().id.to_string())
|
||||
cookies.add_private(Cookie::build(AUTH_COOKIE, user_id)
|
||||
.same_site(SameSite::Lax)
|
||||
.finish());
|
||||
|
||||
|
||||
+6
-6
@@ -5,18 +5,18 @@ use plume_models::{
|
||||
posts::Post,
|
||||
users::User,
|
||||
};
|
||||
use routes::Page;
|
||||
use routes::{Page, errors::ErrorPage};
|
||||
use template_utils::Ructe;
|
||||
|
||||
#[get("/tag/<name>?<page>")]
|
||||
pub fn tag(user: Option<User>, conn: DbConn, name: String, page: Option<Page>, intl: I18n) -> Ructe {
|
||||
pub fn tag(user: Option<User>, conn: DbConn, name: String, page: Option<Page>, intl: I18n) -> Result<Ructe, ErrorPage> {
|
||||
let page = page.unwrap_or_default();
|
||||
let posts = Post::list_by_tag(&*conn, name.clone(), page.limits());
|
||||
render!(tags::index(
|
||||
let posts = Post::list_by_tag(&*conn, name.clone(), page.limits())?;
|
||||
Ok(render!(tags::index(
|
||||
&(&*conn, &intl.catalog, user),
|
||||
name.clone(),
|
||||
posts,
|
||||
page.0,
|
||||
Page::total(Post::count_for_tag(&*conn, name) as i32)
|
||||
))
|
||||
Page::total(Post::count_for_tag(&*conn, name)? as i32)
|
||||
)))
|
||||
}
|
||||
|
||||
+81
-72
@@ -7,6 +7,7 @@ use rocket::{
|
||||
};
|
||||
use rocket_i18n::I18n;
|
||||
use serde_json;
|
||||
use std::{borrow::Cow, collections::HashMap};
|
||||
use validator::{Validate, ValidationError, ValidationErrors};
|
||||
|
||||
use inbox::{Inbox, SignedJson};
|
||||
@@ -18,10 +19,11 @@ use plume_common::activity_pub::{
|
||||
};
|
||||
use plume_common::utils;
|
||||
use plume_models::{
|
||||
Error,
|
||||
blogs::Blog, db_conn::DbConn, follows, headers::Headers, instance::Instance, posts::{LicensedArticle, Post},
|
||||
reshares::Reshare, users::*,
|
||||
};
|
||||
use routes::Page;
|
||||
use routes::{Page, errors::ErrorPage};
|
||||
use template_utils::Ructe;
|
||||
use Worker;
|
||||
use Searcher;
|
||||
@@ -45,24 +47,24 @@ pub fn details(
|
||||
update_conn: DbConn,
|
||||
intl: I18n,
|
||||
searcher: Searcher,
|
||||
) -> Result<Ructe, Ructe> {
|
||||
let user = User::find_by_fqn(&*conn, &name).ok_or_else(|| render!(errors::not_found(&(&*conn, &intl.catalog, account.clone()))))?;
|
||||
let recents = Post::get_recents_for_author(&*conn, &user, 6);
|
||||
let reshares = Reshare::get_recents_for_author(&*conn, &user, 6);
|
||||
) -> Result<Ructe, ErrorPage> {
|
||||
let user = User::find_by_fqn(&*conn, &name)?;
|
||||
let recents = Post::get_recents_for_author(&*conn, &user, 6)?;
|
||||
let reshares = Reshare::get_recents_for_author(&*conn, &user, 6)?;
|
||||
|
||||
if !user.get_instance(&*conn).local {
|
||||
if !user.get_instance(&*conn)?.local {
|
||||
// Fetch new articles
|
||||
let user_clone = user.clone();
|
||||
let searcher = searcher.clone();
|
||||
worker.execute(move || {
|
||||
for create_act in user_clone.fetch_outbox::<Create>() {
|
||||
for create_act in user_clone.fetch_outbox::<Create>().expect("Remote user: outbox couldn't be fetched") {
|
||||
match create_act.create_props.object_object::<LicensedArticle>() {
|
||||
Ok(article) => {
|
||||
Post::from_activity(
|
||||
&(&*fetch_articles_conn, &searcher),
|
||||
article,
|
||||
user_clone.clone().into_id(),
|
||||
);
|
||||
).expect("Article from remote user couldn't be saved");
|
||||
println!("Fetched article from remote user");
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -75,10 +77,10 @@ pub fn details(
|
||||
// Fetch followers
|
||||
let user_clone = user.clone();
|
||||
worker.execute(move || {
|
||||
for user_id in user_clone.fetch_followers_ids() {
|
||||
for user_id in user_clone.fetch_followers_ids().expect("Remote user: fetching followers error") {
|
||||
let follower =
|
||||
User::find_by_ap_url(&*fetch_followers_conn, &user_id)
|
||||
.unwrap_or_else(|| {
|
||||
.unwrap_or_else(|_| {
|
||||
User::fetch_from_url(&*fetch_followers_conn, &user_id)
|
||||
.expect("user::details: Couldn't fetch follower")
|
||||
});
|
||||
@@ -89,7 +91,7 @@ pub fn details(
|
||||
following_id: user_clone.id,
|
||||
ap_url: format!("{}/follow/{}", follower.ap_url, user_clone.ap_url),
|
||||
},
|
||||
);
|
||||
).expect("Couldn't save follower for remote user");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -97,7 +99,7 @@ pub fn details(
|
||||
let user_clone = user.clone();
|
||||
if user.needs_update() {
|
||||
worker.execute(move || {
|
||||
user_clone.refetch(&*update_conn);
|
||||
user_clone.refetch(&*update_conn).expect("Couldn't update user info");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -105,22 +107,22 @@ pub fn details(
|
||||
Ok(render!(users::details(
|
||||
&(&*conn, &intl.catalog, account.clone()),
|
||||
user.clone(),
|
||||
account.map(|x| x.is_following(&*conn, user.id)).unwrap_or(false),
|
||||
user.instance_id != Instance::local_id(&*conn),
|
||||
user.get_instance(&*conn).public_domain,
|
||||
account.and_then(|x| x.is_following(&*conn, user.id).ok()).unwrap_or(false),
|
||||
user.instance_id != Instance::get_local(&*conn)?.id,
|
||||
user.get_instance(&*conn)?.public_domain,
|
||||
recents,
|
||||
reshares.into_iter().map(|r| r.get_post(&*conn).expect("user::details: Reshared post error")).collect()
|
||||
reshares.into_iter().filter_map(|r| r.get_post(&*conn).ok()).collect()
|
||||
)))
|
||||
}
|
||||
|
||||
#[get("/dashboard")]
|
||||
pub fn dashboard(user: User, conn: DbConn, intl: I18n) -> Ructe {
|
||||
let blogs = Blog::find_for_author(&*conn, &user);
|
||||
render!(users::dashboard(
|
||||
pub fn dashboard(user: User, conn: DbConn, intl: I18n) -> Result<Ructe, ErrorPage> {
|
||||
let blogs = Blog::find_for_author(&*conn, &user)?;
|
||||
Ok(render!(users::dashboard(
|
||||
&(&*conn, &intl.catalog, Some(user.clone())),
|
||||
blogs,
|
||||
Post::drafts_by_author(&*conn, &user)
|
||||
))
|
||||
Post::drafts_by_author(&*conn, &user)?
|
||||
)))
|
||||
}
|
||||
|
||||
#[get("/dashboard", rank = 2)]
|
||||
@@ -132,10 +134,10 @@ pub fn dashboard_auth(i18n: I18n) -> Flash<Redirect> {
|
||||
}
|
||||
|
||||
#[post("/@/<name>/follow")]
|
||||
pub fn follow(name: String, conn: DbConn, user: User, worker: Worker) -> Option<Redirect> {
|
||||
pub fn follow(name: String, conn: DbConn, user: User, worker: Worker) -> Result<Redirect, ErrorPage> {
|
||||
let target = User::find_by_fqn(&*conn, &name)?;
|
||||
if let Some(follow) = follows::Follow::find(&*conn, user.id, target.id) {
|
||||
let delete_act = follow.delete(&*conn);
|
||||
if let Ok(follow) = follows::Follow::find(&*conn, user.id, target.id) {
|
||||
let delete_act = follow.delete(&*conn)?;
|
||||
worker.execute(move || {
|
||||
broadcast(&user, delete_act, vec![target])
|
||||
});
|
||||
@@ -147,13 +149,13 @@ pub fn follow(name: String, conn: DbConn, user: User, worker: Worker) -> Option<
|
||||
following_id: target.id,
|
||||
ap_url: format!("{}/follow/{}", user.ap_url, target.ap_url),
|
||||
},
|
||||
);
|
||||
f.notify(&*conn);
|
||||
)?;
|
||||
f.notify(&*conn)?;
|
||||
|
||||
let act = f.to_activity(&*conn);
|
||||
let act = f.to_activity(&*conn)?;
|
||||
worker.execute(move || broadcast(&user, act, vec![target]));
|
||||
}
|
||||
Some(Redirect::to(uri!(details: name = name)))
|
||||
Ok(Redirect::to(uri!(details: name = name)))
|
||||
}
|
||||
|
||||
#[post("/@/<name>/follow", rank = 2)]
|
||||
@@ -165,18 +167,18 @@ pub fn follow_auth(name: String, i18n: I18n) -> Flash<Redirect> {
|
||||
}
|
||||
|
||||
#[get("/@/<name>/followers?<page>", rank = 2)]
|
||||
pub fn followers(name: String, conn: DbConn, account: Option<User>, page: Option<Page>, intl: I18n) -> Result<Ructe, Ructe> {
|
||||
pub fn followers(name: String, conn: DbConn, account: Option<User>, page: Option<Page>, intl: I18n) -> Result<Ructe, ErrorPage> {
|
||||
let page = page.unwrap_or_default();
|
||||
let user = User::find_by_fqn(&*conn, &name).ok_or_else(|| render!(errors::not_found(&(&*conn, &intl.catalog, account.clone()))))?;
|
||||
let followers_count = user.count_followers(&*conn);
|
||||
let user = User::find_by_fqn(&*conn, &name)?;
|
||||
let followers_count = user.count_followers(&*conn)?;
|
||||
|
||||
Ok(render!(users::followers(
|
||||
&(&*conn, &intl.catalog, account.clone()),
|
||||
user.clone(),
|
||||
account.map(|x| x.is_following(&*conn, user.id)).unwrap_or(false),
|
||||
user.instance_id != Instance::local_id(&*conn),
|
||||
user.get_instance(&*conn).public_domain,
|
||||
user.get_followers_page(&*conn, page.limits()),
|
||||
account.and_then(|x| x.is_following(&*conn, user.id).ok()).unwrap_or(false),
|
||||
user.instance_id != Instance::get_local(&*conn)?.id,
|
||||
user.get_instance(&*conn)?.public_domain,
|
||||
user.get_followers_page(&*conn, page.limits())?,
|
||||
page.0,
|
||||
Page::total(followers_count as i32)
|
||||
)))
|
||||
@@ -188,24 +190,24 @@ pub fn activity_details(
|
||||
conn: DbConn,
|
||||
_ap: ApRequest,
|
||||
) -> Option<ActivityStream<CustomPerson>> {
|
||||
let user = User::find_local(&*conn, &name)?;
|
||||
Some(ActivityStream::new(user.to_activity(&*conn)))
|
||||
let user = User::find_local(&*conn, &name).ok()?;
|
||||
Some(ActivityStream::new(user.to_activity(&*conn).ok()?))
|
||||
}
|
||||
|
||||
#[get("/users/new")]
|
||||
pub fn new(user: Option<User>, conn: DbConn, intl: I18n) -> Ructe {
|
||||
render!(users::new(
|
||||
pub fn new(user: Option<User>, conn: DbConn, intl: I18n) -> Result<Ructe, ErrorPage> {
|
||||
Ok(render!(users::new(
|
||||
&(&*conn, &intl.catalog, user),
|
||||
Instance::get_local(&*conn).map(|i| i.open_registrations).unwrap_or(true),
|
||||
Instance::get_local(&*conn)?.open_registrations,
|
||||
&NewUserForm::default(),
|
||||
ValidationErrors::default()
|
||||
))
|
||||
)))
|
||||
}
|
||||
|
||||
#[get("/@/<name>/edit")]
|
||||
pub fn edit(name: String, user: User, conn: DbConn, intl: I18n) -> Option<Ructe> {
|
||||
pub fn edit(name: String, user: User, conn: DbConn, intl: I18n) -> Result<Ructe, ErrorPage> {
|
||||
if user.username == name && !name.contains('@') {
|
||||
Some(render!(users::edit(
|
||||
Ok(render!(users::edit(
|
||||
&(&*conn, &intl.catalog, Some(user.clone())),
|
||||
UpdateUserForm {
|
||||
display_name: user.display_name.clone(),
|
||||
@@ -215,7 +217,7 @@ pub fn edit(name: String, user: User, conn: DbConn, intl: I18n) -> Option<Ructe>
|
||||
ValidationErrors::default()
|
||||
)))
|
||||
} else {
|
||||
None
|
||||
Err(Error::Unauthorized)?
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,29 +237,29 @@ pub struct UpdateUserForm {
|
||||
}
|
||||
|
||||
#[put("/@/<_name>/edit", data = "<form>")]
|
||||
pub fn update(_name: String, conn: DbConn, user: User, form: LenientForm<UpdateUserForm>) -> Redirect {
|
||||
pub fn update(_name: String, conn: DbConn, user: User, form: LenientForm<UpdateUserForm>) -> Result<Redirect, ErrorPage> {
|
||||
user.update(
|
||||
&*conn,
|
||||
if !form.display_name.is_empty() { form.display_name.clone() } else { user.display_name.clone() },
|
||||
if !form.email.is_empty() { form.email.clone() } else { user.email.clone().unwrap_or_default() },
|
||||
if !form.summary.is_empty() { form.summary.clone() } else { user.summary.to_string() },
|
||||
);
|
||||
Redirect::to(uri!(me))
|
||||
)?;
|
||||
Ok(Redirect::to(uri!(me)))
|
||||
}
|
||||
|
||||
#[post("/@/<name>/delete")]
|
||||
pub fn delete(name: String, conn: DbConn, user: User, mut cookies: Cookies, searcher: Searcher) -> Option<Redirect> {
|
||||
pub fn delete(name: String, conn: DbConn, user: User, mut cookies: Cookies, searcher: Searcher) -> Result<Redirect, ErrorPage> {
|
||||
let account = User::find_by_fqn(&*conn, &name)?;
|
||||
if user.id == account.id {
|
||||
account.delete(&*conn, &searcher);
|
||||
account.delete(&*conn, &searcher)?;
|
||||
|
||||
if let Some(cookie) = cookies.get_private(AUTH_COOKIE) {
|
||||
cookies.remove_private(cookie);
|
||||
}
|
||||
if let Some(cookie) = cookies.get_private(AUTH_COOKIE) {
|
||||
cookies.remove_private(cookie);
|
||||
}
|
||||
|
||||
Some(Redirect::to(uri!(super::instance::index)))
|
||||
Ok(Redirect::to(uri!(super::instance::index)))
|
||||
} else {
|
||||
Some(Redirect::to(uri!(edit: name = name)))
|
||||
Ok(Redirect::to(uri!(edit: name = name)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,6 +309,16 @@ pub fn validate_username(username: &str) -> Result<(), ValidationError> {
|
||||
}
|
||||
}
|
||||
|
||||
fn to_validation(_: Error) -> ValidationErrors {
|
||||
let mut errors = ValidationErrors::new();
|
||||
errors.add("", ValidationError {
|
||||
code: Cow::from("server_error"),
|
||||
message: Some(Cow::from("An unknown error occured")),
|
||||
params: HashMap::new()
|
||||
});
|
||||
errors
|
||||
}
|
||||
|
||||
#[post("/users/new", data = "<form>")]
|
||||
pub fn create(conn: DbConn, form: LenientForm<NewUserForm>, intl: I18n) -> Result<Redirect, Ructe> {
|
||||
if !Instance::get_local(&*conn)
|
||||
@@ -320,7 +332,7 @@ pub fn create(conn: DbConn, form: LenientForm<NewUserForm>, intl: I18n) -> Resul
|
||||
form.username = form.username.trim().to_owned();
|
||||
form.email = form.email.trim().to_owned();
|
||||
form.validate()
|
||||
.map(|_| {
|
||||
.and_then(|_| {
|
||||
NewUser::new_local(
|
||||
&*conn,
|
||||
form.username.to_string(),
|
||||
@@ -328,9 +340,9 @@ pub fn create(conn: DbConn, form: LenientForm<NewUserForm>, intl: I18n) -> Resul
|
||||
false,
|
||||
"",
|
||||
form.email.to_string(),
|
||||
User::hash_pass(&form.password),
|
||||
).update_boxes(&*conn);
|
||||
Redirect::to(uri!(super::session::new: m = _))
|
||||
User::hash_pass(&form.password).map_err(to_validation)?,
|
||||
).and_then(|u| u.update_boxes(&*conn)).map_err(to_validation)?;
|
||||
Ok(Redirect::to(uri!(super::session::new: m = _)))
|
||||
})
|
||||
.map_err(|err| {
|
||||
render!(users::new(
|
||||
@@ -344,8 +356,8 @@ pub fn create(conn: DbConn, form: LenientForm<NewUserForm>, intl: I18n) -> Resul
|
||||
|
||||
#[get("/@/<name>/outbox")]
|
||||
pub fn outbox(name: String, conn: DbConn) -> Option<ActivityStream<OrderedCollection>> {
|
||||
let user = User::find_local(&*conn, &name)?;
|
||||
Some(user.outbox(&*conn))
|
||||
let user = User::find_local(&*conn, &name).ok()?;
|
||||
user.outbox(&*conn).ok()
|
||||
}
|
||||
|
||||
#[post("/@/<name>/inbox", data = "<data>")]
|
||||
@@ -356,7 +368,7 @@ pub fn inbox(
|
||||
headers: Headers,
|
||||
searcher: Searcher,
|
||||
) -> Result<String, Option<status::BadRequest<&'static str>>> {
|
||||
let user = User::find_local(&*conn, &name).ok_or(None)?;
|
||||
let user = User::find_local(&*conn, &name).map_err(|_| None)?;
|
||||
let act = data.1.into_inner();
|
||||
|
||||
let activity = act.clone();
|
||||
@@ -378,7 +390,7 @@ pub fn inbox(
|
||||
return Err(Some(status::BadRequest(Some("Invalid signature"))));
|
||||
}
|
||||
|
||||
if Instance::is_blocked(&*conn, actor_id) {
|
||||
if Instance::is_blocked(&*conn, actor_id).map_err(|_| None)? {
|
||||
return Ok(String::new());
|
||||
}
|
||||
Ok(match user.received(&*conn, &searcher, act) {
|
||||
@@ -396,36 +408,33 @@ pub fn ap_followers(
|
||||
conn: DbConn,
|
||||
_ap: ApRequest,
|
||||
) -> Option<ActivityStream<OrderedCollection>> {
|
||||
let user = User::find_local(&*conn, &name)?;
|
||||
let user = User::find_local(&*conn, &name).ok()?;
|
||||
let followers = user
|
||||
.get_followers(&*conn)
|
||||
.get_followers(&*conn).ok()?
|
||||
.into_iter()
|
||||
.map(|f| Id::new(f.ap_url))
|
||||
.collect::<Vec<Id>>();
|
||||
|
||||
let mut coll = OrderedCollection::default();
|
||||
coll.object_props
|
||||
.set_id_string(user.followers_endpoint)
|
||||
.expect("user::ap_followers: id error");
|
||||
.set_id_string(user.followers_endpoint).ok()?;
|
||||
coll.collection_props
|
||||
.set_total_items_u64(followers.len() as u64)
|
||||
.expect("user::ap_followers: totalItems error");
|
||||
.set_total_items_u64(followers.len() as u64).ok()?;
|
||||
coll.collection_props
|
||||
.set_items_link_vec(followers)
|
||||
.expect("user::ap_followers items error");
|
||||
.set_items_link_vec(followers).ok()?;
|
||||
Some(ActivityStream::new(coll))
|
||||
}
|
||||
|
||||
#[get("/@/<name>/atom.xml")]
|
||||
pub fn atom_feed(name: String, conn: DbConn) -> Option<Content<String>> {
|
||||
let author = User::find_by_fqn(&*conn, &name)?;
|
||||
let author = User::find_by_fqn(&*conn, &name).ok()?;
|
||||
let feed = FeedBuilder::default()
|
||||
.title(author.display_name.clone())
|
||||
.id(Instance::get_local(&*conn)
|
||||
.unwrap()
|
||||
.compute_box("~", &name, "atom.xml"))
|
||||
.entries(
|
||||
Post::get_recents_for_author(&*conn, &author, 15)
|
||||
Post::get_recents_for_author(&*conn, &author, 15).ok()?
|
||||
.into_iter()
|
||||
.map(|p| super::post_to_atom(p, &*conn))
|
||||
.collect::<Vec<Entry>>(),
|
||||
|
||||
@@ -35,13 +35,11 @@ impl Resolver<DbConn> for WebfingerResolver {
|
||||
}
|
||||
|
||||
fn find(acct: String, conn: DbConn) -> Result<Webfinger, ResolverError> {
|
||||
match User::find_local(&*conn, &acct) {
|
||||
Some(usr) => Ok(usr.webfinger(&*conn)),
|
||||
None => match Blog::find_local(&*conn, &acct) {
|
||||
Some(blog) => Ok(blog.webfinger(&*conn)),
|
||||
None => Err(ResolverError::NotFound)
|
||||
}
|
||||
}
|
||||
User::find_local(&*conn, &acct)
|
||||
.and_then(|usr| usr.webfinger(&*conn))
|
||||
.or_else(|_| Blog::find_local(&*conn, &acct)
|
||||
.and_then(|blog| blog.webfinger(&*conn))
|
||||
.or(Err(ResolverError::NotFound)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user