add enum containing all successful route returns (#614)

* add enum containing all successful route returns

This enum derives `Responder`, so it can be used as route result.
We also implement `From`, so it can be converted

* disable clippy warning about this enum

There's no use trying to chase this warning.
The next warning will be about `Redirect`, which itself is 360 byte,
according to @fdb-hiroshima (thanks for the research!)

So, if anything, we can try to get Rocket to work on that!

* refactor routes/posts to only use one level of Result

* admin settings error is not an ErrorPage, so dont use Result<>

* refactor: use early return to remove indent of main logic

This diff is absolutely atrocious.
but the resulting readability is worth it

* refactor routes/post for early returns & RespondOrRedirect

* refactor routes/session for early returns & use RespondOrRedirect

* refactor routes/user -- add another field to enum

* refactor routes/blogs for early returns & RespondOrRedirect

* refactor routes/blogs for early returns & RespondOrRedirect

This is a final refactor of `pub fn update()` for readability.
We're removing at least one indentation level.
This commit is contained in:
Igor Galić 2019-06-14 09:33:30 +02:00 committed by GitHub
parent 4b205fa995
commit 3d27e283ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 337 additions and 292 deletions

View File

@ -16,7 +16,7 @@ use plume_models::{
blog_authors::*, blogs::*, instance::Instance, medias::*, posts::Post, safe_string::SafeString, blog_authors::*, blogs::*, instance::Instance, medias::*, posts::Post, safe_string::SafeString,
users::User, Connection, PlumeRocket, users::User, Connection, PlumeRocket,
}; };
use routes::{errors::ErrorPage, Page}; use routes::{errors::ErrorPage, Page, RespondOrRedirect};
use template_utils::{IntoContext, Ructe}; use template_utils::{IntoContext, Ructe};
#[get("/~/<name>?<page>", rank = 2)] #[get("/~/<name>?<page>", rank = 2)]
@ -84,10 +84,7 @@ fn valid_slug(title: &str) -> Result<(), ValidationError> {
} }
#[post("/blogs/new", data = "<form>")] #[post("/blogs/new", data = "<form>")]
pub fn create( pub fn create(form: LenientForm<NewBlogForm>, rockets: PlumeRocket) -> RespondOrRedirect {
form: LenientForm<NewBlogForm>,
rockets: PlumeRocket,
) -> Result<Flash<Redirect>, Ructe> {
let slug = utils::make_actor_id(&form.title); let slug = utils::make_actor_id(&form.title);
let conn = &*rockets.conn; let conn = &*rockets.conn;
let intl = &rockets.intl.catalog; let intl = &rockets.intl.catalog;
@ -111,42 +108,43 @@ pub fn create(
); );
} }
if errors.is_empty() { if !errors.is_empty() {
let blog = Blog::insert( return render!(blogs::new(&rockets.to_context(), &*form, errors)).into();
&*conn,
NewBlog::new_local(
slug.clone(),
form.title.to_string(),
String::from(""),
Instance::get_local()
.expect("blog::create: instance error")
.id,
)
.expect("blog::create: new local error"),
)
.expect("blog::create: error");
BlogAuthor::insert(
&*conn,
NewBlogAuthor {
blog_id: blog.id,
author_id: user.id,
is_owner: true,
},
)
.expect("blog::create: author error");
Ok(Flash::success(
Redirect::to(uri!(details: name = slug.clone(), page = _)),
&i18n!(intl, "Your blog was successfully created!"),
))
} else {
Err(render!(blogs::new(&rockets.to_context(), &*form, errors)))
} }
let blog = Blog::insert(
&*conn,
NewBlog::new_local(
slug.clone(),
form.title.to_string(),
String::from(""),
Instance::get_local()
.expect("blog::create: instance error")
.id,
)
.expect("blog::create: new local error"),
)
.expect("blog::create: error");
BlogAuthor::insert(
&*conn,
NewBlogAuthor {
blog_id: blog.id,
author_id: user.id,
is_owner: true,
},
)
.expect("blog::create: author error");
Flash::success(
Redirect::to(uri!(details: name = slug.clone(), page = _)),
&i18n!(intl, "Your blog was successfully created!"),
)
.into()
} }
#[post("/~/<name>/delete")] #[post("/~/<name>/delete")]
pub fn delete(name: String, rockets: PlumeRocket) -> Result<Flash<Redirect>, Ructe> { pub fn delete(name: String, rockets: PlumeRocket) -> RespondOrRedirect {
let conn = &*rockets.conn; let conn = &*rockets.conn;
let blog = Blog::find_by_fqn(&rockets, &name).expect("blog::delete: blog not found"); let blog = Blog::find_by_fqn(&rockets, &name).expect("blog::delete: blog not found");
@ -158,19 +156,21 @@ pub fn delete(name: String, rockets: PlumeRocket) -> Result<Flash<Redirect>, Ruc
{ {
blog.delete(&conn, &rockets.searcher) blog.delete(&conn, &rockets.searcher)
.expect("blog::expect: deletion error"); .expect("blog::expect: deletion error");
Ok(Flash::success( Flash::success(
Redirect::to(uri!(super::instance::index)), Redirect::to(uri!(super::instance::index)),
i18n!(rockets.intl.catalog, "Your blog was deleted."), i18n!(rockets.intl.catalog, "Your blog was deleted."),
)) )
.into()
} else { } else {
// TODO actually return 403 error code // TODO actually return 403 error code
Err(render!(errors::not_authorized( render!(errors::not_authorized(
&rockets.to_context(), &rockets.to_context(),
i18n!( i18n!(
rockets.intl.catalog, rockets.intl.catalog,
"You are not allowed to delete this blog." "You are not allowed to delete this blog."
) )
))) ))
.into()
} }
} }
@ -236,104 +236,107 @@ pub fn update(
name: String, name: String,
form: LenientForm<EditForm>, form: LenientForm<EditForm>,
rockets: PlumeRocket, rockets: PlumeRocket,
) -> Result<Flash<Redirect>, Ructe> { ) -> RespondOrRedirect {
let conn = &*rockets.conn; let conn = &*rockets.conn;
let intl = &rockets.intl.catalog; let intl = &rockets.intl.catalog;
let mut blog = Blog::find_by_fqn(&rockets, &name).expect("blog::update: blog not found"); let mut blog = Blog::find_by_fqn(&rockets, &name).expect("blog::update: blog not found");
if rockets if !rockets
.user .user
.clone() .clone()
.and_then(|u| u.is_author_in(&*conn, &blog).ok()) .and_then(|u| u.is_author_in(&*conn, &blog).ok())
.unwrap_or(false) .unwrap_or(false)
{ {
let user = rockets
.user
.clone()
.expect("blogs::edit: User was None while it shouldn't");
form.validate()
.and_then(|_| {
if let Some(icon) = form.icon {
if !check_media(&*conn, icon, &user) {
let mut errors = ValidationErrors::new();
errors.add(
"",
ValidationError {
code: Cow::from("icon"),
message: Some(Cow::from(i18n!(
intl,
"You can't use this media as a blog icon."
))),
params: HashMap::new(),
},
);
return Err(errors);
}
}
if let Some(banner) = form.banner {
if !check_media(&*conn, banner, &user) {
let mut errors = ValidationErrors::new();
errors.add(
"",
ValidationError {
code: Cow::from("banner"),
message: Some(Cow::from(i18n!(
intl,
"You can't use this media as a blog banner."
))),
params: HashMap::new(),
},
);
return Err(errors);
}
}
blog.title = form.title.clone();
blog.summary = form.summary.clone();
blog.summary_html = SafeString::new(
&utils::md_to_html(
&form.summary,
None,
true,
Some(Media::get_media_processor(
&conn,
blog.list_authors(&conn)
.expect("Couldn't get list of authors")
.iter()
.collect(),
)),
)
.0,
);
blog.icon_id = form.icon;
blog.banner_id = form.banner;
blog.save_changes::<Blog>(&*conn)
.expect("Couldn't save blog changes");
Ok(Flash::success(
Redirect::to(uri!(details: name = name, page = _)),
i18n!(intl, "Your blog information have been updated."),
))
})
.map_err(|err| {
let medias = Media::for_user(&*conn, user.id).expect("Couldn't list media");
render!(blogs::edit(
&rockets.to_context(),
&blog,
medias,
&*form,
err
))
})
} else {
// TODO actually return 403 error code // TODO actually return 403 error code
Err(render!(errors::not_authorized( return render!(errors::not_authorized(
&rockets.to_context(), &rockets.to_context(),
i18n!( i18n!(
rockets.intl.catalog, rockets.intl.catalog,
"You are not allowed to edit this blog." "You are not allowed to edit this blog."
) )
))) ))
.into();
} }
let user = rockets
.user
.clone()
.expect("blogs::edit: User was None while it shouldn't");
form.validate()
.and_then(|_| {
if let Some(icon) = form.icon {
if !check_media(&*conn, icon, &user) {
let mut errors = ValidationErrors::new();
errors.add(
"",
ValidationError {
code: Cow::from("icon"),
message: Some(Cow::from(i18n!(
intl,
"You can't use this media as a blog icon."
))),
params: HashMap::new(),
},
);
return Err(errors);
}
}
if let Some(banner) = form.banner {
if !check_media(&*conn, banner, &user) {
let mut errors = ValidationErrors::new();
errors.add(
"",
ValidationError {
code: Cow::from("banner"),
message: Some(Cow::from(i18n!(
intl,
"You can't use this media as a blog banner."
))),
params: HashMap::new(),
},
);
return Err(errors);
}
}
blog.title = form.title.clone();
blog.summary = form.summary.clone();
blog.summary_html = SafeString::new(
&utils::md_to_html(
&form.summary,
None,
true,
Some(Media::get_media_processor(
&conn,
blog.list_authors(&conn)
.expect("Couldn't get list of authors")
.iter()
.collect(),
)),
)
.0,
);
blog.icon_id = form.icon;
blog.banner_id = form.banner;
blog.save_changes::<Blog>(&*conn)
.expect("Couldn't save blog changes");
Ok(Flash::success(
Redirect::to(uri!(details: name = name, page = _)),
i18n!(intl, "Your blog information have been updated."),
))
})
.map_err(|err| {
let medias = Media::for_user(&*conn, user.id).expect("Couldn't list media");
render!(blogs::edit(
&rockets.to_context(),
&blog,
medias,
&*form,
err
))
})
.unwrap()
.into()
} }
#[get("/~/<name>/outbox")] #[get("/~/<name>/outbox")]

View File

@ -13,7 +13,7 @@ use plume_models::{
admin::Admin, comments::Comment, db_conn::DbConn, headers::Headers, instance::*, posts::Post, admin::Admin, comments::Comment, db_conn::DbConn, headers::Headers, instance::*, posts::Post,
safe_string::SafeString, users::User, Error, PlumeRocket, CONFIG, safe_string::SafeString, users::User, Error, PlumeRocket, CONFIG,
}; };
use routes::{errors::ErrorPage, rocket_uri_macro_static_files, Page}; use routes::{errors::ErrorPage, rocket_uri_macro_static_files, Page, RespondOrRedirect};
use template_utils::{IntoContext, Ructe}; use template_utils::{IntoContext, Ructe};
#[get("/")] #[get("/")]
@ -114,36 +114,36 @@ pub fn update_settings(
_admin: Admin, _admin: Admin,
form: LenientForm<InstanceSettingsForm>, form: LenientForm<InstanceSettingsForm>,
rockets: PlumeRocket, rockets: PlumeRocket,
) -> Result<Flash<Redirect>, Ructe> { ) -> RespondOrRedirect {
let conn = &*rockets.conn; let conn = &*rockets.conn;
form.validate() if let Err(e) = form.validate() {
.and_then(|_| { let local_inst =
let instance = Instance::get_local().expect("instance::update_settings: local instance error");
Instance::get_local().expect("instance::update_settings: local instance error"); render!(instance::admin(
instance &rockets.to_context(),
.update( local_inst,
conn, form.clone(),
form.name.clone(), e
form.open_registrations, ))
form.short_description.clone(), .into()
form.long_description.clone(), } else {
) let instance =
.expect("instance::update_settings: save error"); Instance::get_local().expect("instance::update_settings: local instance error");
Ok(Flash::success( instance
Redirect::to(uri!(admin)), .update(
i18n!(rockets.intl.catalog, "Instance settings have been saved."), conn,
)) form.name.clone(),
}) form.open_registrations,
.or_else(|e| { form.short_description.clone(),
let local_inst = form.long_description.clone(),
Instance::get_local().expect("instance::update_settings: local instance error"); )
Err(render!(instance::admin( .expect("instance::update_settings: save error");
&rockets.to_context(), Flash::success(
local_inst, Redirect::to(uri!(admin)),
form.clone(), i18n!(rockets.intl.catalog, "Instance settings have been saved."),
e )
))) .into()
}) }
} }
#[get("/admin/instances?<page>")] #[get("/admin/instances?<page>")]

View File

@ -38,76 +38,75 @@ pub fn upload(
ct: &ContentType, ct: &ContentType,
conn: DbConn, conn: DbConn,
) -> Result<Redirect, status::BadRequest<&'static str>> { ) -> Result<Redirect, status::BadRequest<&'static str>> {
if ct.is_form_data() { if !ct.is_form_data() {
let (_, boundary) = ct return Ok(Redirect::to(uri!(new)));
.params() }
.find(|&(k, _)| k == "boundary")
.ok_or_else(|| status::BadRequest(Some("No boundary")))?;
match Multipart::with_body(data.open(), boundary).save().temp() { let (_, boundary) = ct
SaveResult::Full(entries) => { .params()
let fields = entries.fields; .find(|&(k, _)| k == "boundary")
.ok_or_else(|| status::BadRequest(Some("No boundary")))?;
let filename = fields if let SaveResult::Full(entries) = Multipart::with_body(data.open(), boundary).save().temp() {
.get("file") let fields = entries.fields;
.and_then(|v| v.iter().next())
.ok_or_else(|| status::BadRequest(Some("No file uploaded")))?
.headers
.filename
.clone();
// Remove extension if it contains something else than just letters and numbers
let ext = filename
.and_then(|f| {
f.rsplit('.')
.next()
.and_then(|ext| {
if ext.chars().any(|c| !c.is_alphanumeric()) {
None
} else {
Some(ext.to_lowercase())
}
})
.map(|ext| format!(".{}", ext))
})
.unwrap_or_default();
let dest = format!("static/media/{}{}", GUID::rand().to_string(), ext);
match fields["file"][0].data { let filename = fields
SavedData::Bytes(ref bytes) => fs::write(&dest, bytes) .get("file")
.map_err(|_| status::BadRequest(Some("Couldn't save upload")))?, .and_then(|v| v.iter().next())
SavedData::File(ref path, _) => { .ok_or_else(|| status::BadRequest(Some("No file uploaded")))?
fs::copy(path, &dest) .headers
.map_err(|_| status::BadRequest(Some("Couldn't copy upload")))?; .filename
} .clone();
_ => { // Remove extension if it contains something else than just letters and numbers
return Ok(Redirect::to(uri!(new))); let ext = filename
} .and_then(|f| {
} f.rsplit('.')
.next()
let has_cw = !read(&fields["cw"][0].data) .and_then(|ext| {
.map(|cw| cw.is_empty()) if ext.chars().any(|c| !c.is_alphanumeric()) {
.unwrap_or(false);
let media = Media::insert(
&*conn,
NewMedia {
file_path: dest,
alt_text: read(&fields["alt"][0].data)?,
is_remote: false,
remote_url: None,
sensitive: has_cw,
content_warning: if has_cw {
Some(read(&fields["cw"][0].data)?)
} else {
None None
}, } else {
owner_id: user.id, Some(ext.to_lowercase())
}, }
) })
.map_err(|_| status::BadRequest(Some("Error while saving media")))?; .map(|ext| format!(".{}", ext))
Ok(Redirect::to(uri!(details: id = media.id))) })
.unwrap_or_default();
let dest = format!("static/media/{}{}", GUID::rand().to_string(), ext);
match fields["file"][0].data {
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")))?;
}
_ => {
return Ok(Redirect::to(uri!(new)));
} }
SaveResult::Partial(_, _) | SaveResult::Error(_) => Ok(Redirect::to(uri!(new))),
} }
let has_cw = !read(&fields["cw"][0].data)
.map(|cw| cw.is_empty())
.unwrap_or(false);
let media = Media::insert(
&*conn,
NewMedia {
file_path: dest,
alt_text: read(&fields["alt"][0].data)?,
is_remote: false,
remote_url: None,
sensitive: has_cw,
content_warning: if has_cw {
Some(read(&fields["cw"][0].data)?)
} else {
None
},
owner_id: user.id,
},
)
.map_err(|_| status::BadRequest(Some("Error while saving media")))?;
Ok(Redirect::to(uri!(details: id = media.id)))
} else { } else {
Ok(Redirect::to(uri!(new))) Ok(Redirect::to(uri!(new)))
} }

View File

@ -7,15 +7,51 @@ use rocket::{
RawStr, Status, RawStr, Status,
}, },
request::{self, FromFormValue, FromRequest, Request}, request::{self, FromFormValue, FromRequest, Request},
response::NamedFile, response::{Flash, NamedFile, Redirect},
Outcome, Outcome,
}; };
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use template_utils::Ructe;
use plume_models::{posts::Post, Connection}; use plume_models::{posts::Post, Connection};
const ITEMS_PER_PAGE: i32 = 12; const ITEMS_PER_PAGE: i32 = 12;
/// Special return type used for routes that "cannot fail", and instead
/// `Redirect`, or `Flash<Redirect>`, when we cannot deliver a `Ructe` Response
#[allow(clippy::large_enum_variant)]
#[derive(Responder)]
pub enum RespondOrRedirect {
Response(Ructe),
FlashResponse(Flash<Ructe>),
Redirect(Redirect),
FlashRedirect(Flash<Redirect>),
}
impl From<Ructe> for RespondOrRedirect {
fn from(response: Ructe) -> Self {
RespondOrRedirect::Response(response)
}
}
impl From<Flash<Ructe>> for RespondOrRedirect {
fn from(response: Flash<Ructe>) -> Self {
RespondOrRedirect::FlashResponse(response)
}
}
impl From<Redirect> for RespondOrRedirect {
fn from(redirect: Redirect) -> Self {
RespondOrRedirect::Redirect(redirect)
}
}
impl From<Flash<Redirect>> for RespondOrRedirect {
fn from(redirect: Flash<Redirect>) -> Self {
RespondOrRedirect::FlashRedirect(redirect)
}
}
#[derive(Shrinkwrap, Copy, Clone, UriDisplayQuery)] #[derive(Shrinkwrap, Copy, Clone, UriDisplayQuery)]
pub struct Page(i32); pub struct Page(i32);

View File

@ -26,7 +26,9 @@ use plume_models::{
users::User, users::User,
Error, PlumeRocket, Error, PlumeRocket,
}; };
use routes::{comments::NewCommentForm, errors::ErrorPage, ContentLen, RemoteForm}; use routes::{
comments::NewCommentForm, errors::ErrorPage, ContentLen, RemoteForm, RespondOrRedirect,
};
use template_utils::{IntoContext, Ructe}; use template_utils::{IntoContext, Ructe};
#[get("/~/<blog>/<slug>?<responding_to>", rank = 4)] #[get("/~/<blog>/<slug>?<responding_to>", rank = 4)]
@ -40,17 +42,23 @@ pub fn details(
let user = rockets.user.clone(); let user = rockets.user.clone();
let blog = Blog::find_by_fqn(&rockets, &blog)?; let blog = Blog::find_by_fqn(&rockets, &blog)?;
let post = Post::find_by_slug(&*conn, &slug, blog.id)?; let post = Post::find_by_slug(&*conn, &slug, blog.id)?;
if post.published if !(post.published
|| post || post
.get_authors(&*conn)? .get_authors(&*conn)?
.into_iter() .into_iter()
.any(|a| a.id == user.clone().map(|u| u.id).unwrap_or(0)) .any(|a| a.id == user.clone().map(|u| u.id).unwrap_or(0)))
{ {
let comments = CommentTree::from_post(&*conn, &post, user.as_ref())?; return Ok(render!(errors::not_authorized(
&rockets.to_context(),
i18n!(rockets.intl.catalog, "This post isn't published yet.")
)));
}
let previous = responding_to.and_then(|r| Comment::get(&*conn, r).ok()); let comments = CommentTree::from_post(&*conn, &post, user.as_ref())?;
Ok(render!(posts::details( let previous = responding_to.and_then(|r| Comment::get(&*conn, r).ok());
Ok(render!(posts::details(
&rockets.to_context(), &rockets.to_context(),
post.clone(), post.clone(),
blog, blog,
@ -87,12 +95,6 @@ pub fn details(
user.and_then(|u| u.is_following(&*conn, post.get_authors(&*conn).ok()?[0].id).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() post.get_authors(&*conn)?[0].clone()
))) )))
} else {
Ok(render!(errors::not_authorized(
&rockets.to_context(),
i18n!(rockets.intl.catalog, "This post isn't published yet.")
)))
}
} }
#[get("/~/<blog>/<slug>", rank = 3)] #[get("/~/<blog>/<slug>", rank = 3)]
@ -219,7 +221,7 @@ pub fn update(
cl: ContentLen, cl: ContentLen,
form: LenientForm<NewPostForm>, form: LenientForm<NewPostForm>,
rockets: PlumeRocket, rockets: PlumeRocket,
) -> Result<Flash<Redirect>, Ructe> { ) -> RespondOrRedirect {
let conn = &*rockets.conn; let conn = &*rockets.conn;
let b = Blog::find_by_fqn(&rockets, &blog).expect("post::update: blog error"); let b = Blog::find_by_fqn(&rockets, &blog).expect("post::update: blog error");
let mut post = let mut post =
@ -255,10 +257,11 @@ pub fn update(
.expect("posts::update: is author in error") .expect("posts::update: is author in error")
{ {
// actually it's not "Ok"… // actually it's not "Ok"…
Ok(Flash::error( Flash::error(
Redirect::to(uri!(super::blogs::details: name = blog, page = _)), Redirect::to(uri!(super::blogs::details: name = blog, page = _)),
i18n!(&intl, "You are not allowed to publish on this blog."), i18n!(&intl, "You are not allowed to publish on this blog."),
)) )
.into()
} else { } else {
let (content, mentions, hashtags) = utils::md_to_html( let (content, mentions, hashtags) = utils::md_to_html(
form.content.to_string().as_ref(), form.content.to_string().as_ref(),
@ -345,14 +348,15 @@ pub fn update(
} }
} }
Ok(Flash::success( Flash::success(
Redirect::to(uri!(details: blog = blog, slug = new_slug, responding_to = _)), Redirect::to(uri!(details: blog = blog, slug = new_slug, responding_to = _)),
i18n!(intl, "Your article has been updated."), i18n!(intl, "Your article has been updated."),
)) )
.into()
} }
} else { } else {
let medias = Media::for_user(&*conn, user.id).expect("posts:update: medias error"); let medias = Media::for_user(&*conn, user.id).expect("posts:update: medias error");
Err(render!(posts::new( render!(posts::new(
&rockets.to_context(), &rockets.to_context(),
i18n!(intl, "Edit {0}"; &form.title), i18n!(intl, "Edit {0}"; &form.title),
b, b,
@ -363,7 +367,8 @@ pub fn update(
errors.clone(), errors.clone(),
medias.clone(), medias.clone(),
cl.0 cl.0
))) ))
.into()
} }
} }
@ -396,7 +401,7 @@ pub fn create(
form: LenientForm<NewPostForm>, form: LenientForm<NewPostForm>,
cl: ContentLen, cl: ContentLen,
rockets: PlumeRocket, rockets: PlumeRocket,
) -> Result<Flash<Redirect>, Result<Ructe, ErrorPage>> { ) -> Result<RespondOrRedirect, ErrorPage> {
let conn = &*rockets.conn; let conn = &*rockets.conn;
let blog = Blog::find_by_fqn(&rockets, &blog_name).expect("post::create: blog error");; let blog = Blog::find_by_fqn(&rockets, &blog_name).expect("post::create: blog error");;
let slug = form.title.to_string().to_kebab_case(); let slug = form.title.to_string().to_kebab_case();
@ -429,7 +434,8 @@ pub fn create(
&rockets.intl.catalog, &rockets.intl.catalog,
"You are not allowed to publish on this blog." "You are not allowed to publish on this blog."
), ),
)); )
.into());
} }
let (content, mentions, hashtags) = utils::md_to_html( let (content, mentions, hashtags) = utils::md_to_html(
@ -530,10 +536,11 @@ pub fn create(
Ok(Flash::success( Ok(Flash::success(
Redirect::to(uri!(details: blog = blog_name, slug = slug, responding_to = _)), Redirect::to(uri!(details: blog = blog_name, slug = slug, responding_to = _)),
i18n!(&rockets.intl.catalog, "Your article has been saved."), i18n!(&rockets.intl.catalog, "Your article has been saved."),
)) )
.into())
} else { } else {
let medias = Media::for_user(&*conn, user.id).expect("posts::create: medias error"); let medias = Media::for_user(&*conn, user.id).expect("posts::create: medias error");
Err(Ok(render!(posts::new( Ok(render!(posts::new(
&rockets.to_context(), &rockets.to_context(),
i18n!(rockets.intl.catalog, "New article"), i18n!(rockets.intl.catalog, "New article"),
blog, blog,
@ -544,7 +551,8 @@ pub fn create(
errors.clone(), errors.clone(),
medias, medias,
cl.0 cl.0
)))) ))
.into())
} }
} }
@ -627,14 +635,14 @@ pub fn remote_interact_post(
blog_name: String, blog_name: String,
slug: String, slug: String,
remote: LenientForm<RemoteForm>, remote: LenientForm<RemoteForm>,
) -> Result<Result<Ructe, Redirect>, ErrorPage> { ) -> Result<RespondOrRedirect, ErrorPage> {
let target = Blog::find_by_fqn(&rockets, &blog_name) let target = Blog::find_by_fqn(&rockets, &blog_name)
.and_then(|blog| Post::find_by_slug(&rockets.conn, &slug, blog.id))?; .and_then(|blog| Post::find_by_slug(&rockets.conn, &slug, blog.id))?;
if let Some(uri) = User::fetch_remote_interact_uri(&remote.remote) if let Some(uri) = User::fetch_remote_interact_uri(&remote.remote)
.ok() .ok()
.and_then(|uri| rt_format!(uri, uri = target.ap_url).ok()) .and_then(|uri| rt_format!(uri, uri = target.ap_url).ok())
{ {
Ok(Err(Redirect::to(uri))) Ok(Redirect::to(uri).into())
} else { } else {
let mut errs = ValidationErrors::new(); let mut errs = ValidationErrors::new();
errs.add("remote", ValidationError { errs.add("remote", ValidationError {
@ -643,13 +651,14 @@ pub fn remote_interact_post(
params: HashMap::new(), params: HashMap::new(),
}); });
//could not get your remote url? //could not get your remote url?
Ok(Ok(render!(posts::remote_interact( Ok(render!(posts::remote_interact(
&rockets.to_context(), &rockets.to_context(),
target, target,
super::session::LoginForm::default(), super::session::LoginForm::default(),
ValidationErrors::default(), ValidationErrors::default(),
remote.clone(), remote.clone(),
errs errs
)))) ))
.into())
} }
} }

View File

@ -7,6 +7,7 @@ use rocket::{
State, State,
}; };
use rocket_i18n::I18n; use rocket_i18n::I18n;
use routes::RespondOrRedirect;
use std::{ use std::{
borrow::Cow, borrow::Cow,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
@ -45,7 +46,7 @@ pub fn create(
form: LenientForm<LoginForm>, form: LenientForm<LoginForm>,
mut cookies: Cookies, mut cookies: Cookies,
rockets: PlumeRocket, rockets: PlumeRocket,
) -> Result<Flash<Redirect>, Ructe> { ) -> RespondOrRedirect {
let conn = &*rockets.conn; let conn = &*rockets.conn;
let user = User::find_by_email(&*conn, &form.email_or_name) let user = User::find_by_email(&*conn, &form.email_or_name)
.or_else(|_| User::find_by_fqn(&rockets, &form.email_or_name)); .or_else(|_| User::find_by_fqn(&rockets, &form.email_or_name));
@ -76,48 +77,43 @@ pub fn create(
String::new() String::new()
}; };
if errors.is_empty() { if !errors.is_empty() {
cookies.add_private( return render!(session::login(&rockets.to_context(), None, &*form, errors)).into();
Cookie::build(AUTH_COOKIE, user_id) }
.same_site(SameSite::Lax)
.finish(),
);
let destination = rockets
.flash_msg
.clone()
.and_then(
|(name, msg)| {
if name == "callback" {
Some(msg)
} else {
None
}
},
)
.unwrap_or_else(|| "/".to_owned());
let uri = Uri::parse(&destination) cookies.add_private(
.map(IntoOwned::into_owned) Cookie::build(AUTH_COOKIE, user_id)
.map_err(|_| { .same_site(SameSite::Lax)
render!(session::login( .finish(),
&(conn, &rockets.intl.catalog, None, None), );
None, let destination = rockets
&*form, .flash_msg
errors .clone()
)) .and_then(
})?; |(name, msg)| {
if name == "callback" {
Some(msg)
} else {
None
}
},
)
.unwrap_or_else(|| "/".to_owned());
Ok(Flash::success( if let Ok(uri) = Uri::parse(&destination).map(IntoOwned::into_owned) {
Flash::success(
Redirect::to(uri), Redirect::to(uri),
i18n!(&rockets.intl.catalog, "You are now connected."), i18n!(&rockets.intl.catalog, "You are now connected."),
)) )
.into()
} else { } else {
Err(render!(session::login( render!(session::login(
&rockets.to_context(), &(conn, &rockets.intl.catalog, None, None),
None, None,
&*form, &*form,
errors errors
))) ))
.into()
} }
} }

View File

@ -25,14 +25,14 @@ use plume_models::{
users::*, users::*,
Error, PlumeRocket, Error, PlumeRocket,
}; };
use routes::{errors::ErrorPage, Page, RemoteForm}; use routes::{errors::ErrorPage, Page, RemoteForm, RespondOrRedirect};
use template_utils::{IntoContext, Ructe}; use template_utils::{IntoContext, Ructe};
#[get("/me")] #[get("/me")]
pub fn me(user: Option<User>) -> Result<Redirect, Flash<Redirect>> { pub fn me(user: Option<User>) -> RespondOrRedirect {
match user { match user {
Some(user) => Ok(Redirect::to(uri!(details: name = user.username))), Some(user) => Redirect::to(uri!(details: name = user.username)).into(),
None => Err(utils::requires_login("", uri!(me))), None => utils::requires_login("", uri!(me)).into(),
} }
} }
@ -190,7 +190,7 @@ pub fn follow_not_connected(
name: String, name: String,
remote_form: Option<LenientForm<RemoteForm>>, remote_form: Option<LenientForm<RemoteForm>>,
i18n: I18n, i18n: I18n,
) -> Result<Result<Flash<Ructe>, Redirect>, ErrorPage> { ) -> Result<RespondOrRedirect, ErrorPage> {
let target = User::find_by_fqn(&rockets, &name)?; let target = User::find_by_fqn(&rockets, &name)?;
if let Some(remote_form) = remote_form { if let Some(remote_form) = remote_form {
if let Some(uri) = User::fetch_remote_interact_uri(&remote_form) if let Some(uri) = User::fetch_remote_interact_uri(&remote_form)
@ -207,7 +207,7 @@ pub fn follow_not_connected(
.ok() .ok()
}) })
{ {
Ok(Err(Redirect::to(uri))) Ok(Redirect::to(uri).into())
} else { } else {
let mut err = ValidationErrors::default(); let mut err = ValidationErrors::default();
err.add("remote", err.add("remote",
@ -217,7 +217,7 @@ pub fn follow_not_connected(
params: HashMap::new(), params: HashMap::new(),
}, },
); );
Ok(Ok(Flash::new( Ok(Flash::new(
render!(users::follow_remote( render!(users::follow_remote(
&rockets.to_context(), &rockets.to_context(),
target, target,
@ -228,10 +228,11 @@ pub fn follow_not_connected(
)), )),
"callback", "callback",
uri!(follow: name = name).to_string(), uri!(follow: name = name).to_string(),
))) )
.into())
} }
} else { } else {
Ok(Ok(Flash::new( Ok(Flash::new(
render!(users::follow_remote( render!(users::follow_remote(
&rockets.to_context(), &rockets.to_context(),
target, target,
@ -243,7 +244,8 @@ pub fn follow_not_connected(
)), )),
"callback", "callback",
uri!(follow: name = name).to_string(), uri!(follow: name = name).to_string(),
))) )
.into())
} }
} }