Normalize panic message and return 400 or 404 when suitable
This commit is contained in:
parent
4e6f3209d5
commit
fd92383f87
@ -19,7 +19,7 @@ fn get(id: i32, conn: DbConn) -> Json<serde_json::Value> {
|
|||||||
|
|
||||||
#[get("/posts")]
|
#[get("/posts")]
|
||||||
fn list(conn: DbConn, uri: &Origin) -> Json<serde_json::Value> {
|
fn list(conn: DbConn, uri: &Origin) -> Json<serde_json::Value> {
|
||||||
let query: PostEndpoint = serde_qs::from_str(uri.query().unwrap_or("")).expect("Invalid query string");
|
let query: PostEndpoint = serde_qs::from_str(uri.query().unwrap_or("")).expect("api::list: invalid query error");
|
||||||
let post = <Post as Provider<Connection>>::list(&*conn, query);
|
let post = <Post as Provider<Connection>>::list(&*conn, query);
|
||||||
Json(json!(post))
|
Json(json!(post))
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ use plume_models::{
|
|||||||
|
|
||||||
pub trait Inbox {
|
pub trait Inbox {
|
||||||
fn received(&self, conn: &Connection, act: serde_json::Value) -> Result<(), Error> {
|
fn received(&self, conn: &Connection, act: serde_json::Value) -> Result<(), Error> {
|
||||||
let actor_id = Id::new(act["actor"].as_str().unwrap_or_else(|| act["actor"]["id"].as_str().expect("No actor ID for incoming activity")));
|
let actor_id = Id::new(act["actor"].as_str().unwrap_or_else(|| act["actor"]["id"].as_str().expect("Inbox::received: actor_id missing error")));
|
||||||
match act["type"].as_str() {
|
match act["type"].as_str() {
|
||||||
Some(t) => {
|
Some(t) => {
|
||||||
match t {
|
match t {
|
||||||
@ -47,7 +47,7 @@ pub trait Inbox {
|
|||||||
},
|
},
|
||||||
"Undo" => {
|
"Undo" => {
|
||||||
let act: Undo = serde_json::from_value(act.clone())?;
|
let act: Undo = serde_json::from_value(act.clone())?;
|
||||||
match act.undo_props.object["type"].as_str().unwrap() {
|
match act.undo_props.object["type"].as_str().expect("Inbox::received: undo without original type error") {
|
||||||
"Like" => {
|
"Like" => {
|
||||||
likes::Like::delete_id(act.undo_props.object_object::<Like>()?.object_props.id_string()?, conn);
|
likes::Like::delete_id(act.undo_props.object_object::<Like>()?.object_props.id_string()?, conn);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -56,7 +56,7 @@ fn init_pool() -> Option<DbPool> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let pool = init_pool().expect("Couldn't intialize database pool");
|
let pool = init_pool().expect("main: database pool initialization error");
|
||||||
rocket::ignite()
|
rocket::ignite()
|
||||||
.mount("/", routes![
|
.mount("/", routes![
|
||||||
routes::blogs::paginated_details,
|
routes::blogs::paginated_details,
|
||||||
@ -176,6 +176,6 @@ fn main() {
|
|||||||
("/login".to_owned(), "/login".to_owned(), rocket::http::Method::Post),
|
("/login".to_owned(), "/login".to_owned(), rocket::http::Method::Post),
|
||||||
("/users/new".to_owned(), "/users/new".to_owned(), rocket::http::Method::Post),
|
("/users/new".to_owned(), "/users/new".to_owned(), rocket::http::Method::Post),
|
||||||
])
|
])
|
||||||
.finalize().unwrap())
|
.finalize().expect("main: csrf fairing creation error"))
|
||||||
.launch();
|
.launch();
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use activitypub::collection::OrderedCollection;
|
use activitypub::collection::OrderedCollection;
|
||||||
use atom_syndication::{Entry, FeedBuilder};
|
use atom_syndication::{Entry, FeedBuilder};
|
||||||
use rocket::{
|
use rocket::{
|
||||||
|
http::ContentType,
|
||||||
request::LenientForm,
|
request::LenientForm,
|
||||||
response::{Redirect, Flash, content::Content},
|
response::{Redirect, Flash, content::Content}
|
||||||
http::ContentType
|
|
||||||
};
|
};
|
||||||
use rocket_contrib::Template;
|
use rocket_contrib::Template;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
@ -49,9 +49,9 @@ fn details(name: String, conn: DbConn, user: Option<User>) -> Template {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[get("/~/<name>", rank = 1)]
|
#[get("/~/<name>", rank = 1)]
|
||||||
fn activity_details(name: String, conn: DbConn, _ap: ApRequest) -> ActivityStream<CustomGroup> {
|
fn activity_details(name: String, conn: DbConn, _ap: ApRequest) -> Option<ActivityStream<CustomGroup>> {
|
||||||
let blog = Blog::find_local(&*conn, name).unwrap();
|
let blog = Blog::find_local(&*conn, name)?;
|
||||||
ActivityStream::new(blog.into_activity(&*conn))
|
Some(ActivityStream::new(blog.into_activity(&*conn)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/blogs/new")]
|
#[get("/blogs/new")]
|
||||||
@ -130,22 +130,23 @@ fn create(conn: DbConn, data: LenientForm<NewBlogForm>, user: User) -> Result<Re
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[get("/~/<name>/outbox")]
|
#[get("/~/<name>/outbox")]
|
||||||
fn outbox(name: String, conn: DbConn) -> ActivityStream<OrderedCollection> {
|
fn outbox(name: String, conn: DbConn) -> Option<ActivityStream<OrderedCollection>> {
|
||||||
let blog = Blog::find_local(&*conn, name).unwrap();
|
let blog = Blog::find_local(&*conn, name)?;
|
||||||
blog.outbox(&*conn)
|
Some(blog.outbox(&*conn))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/~/<name>/atom.xml")]
|
#[get("/~/<name>/atom.xml")]
|
||||||
fn atom_feed(name: String, conn: DbConn) -> Content<String> {
|
fn atom_feed(name: String, conn: DbConn) -> Option<Content<String>> {
|
||||||
let blog = Blog::find_by_fqn(&*conn, name.clone()).expect("Unable to find blog");
|
let blog = Blog::find_by_fqn(&*conn, name.clone())?;
|
||||||
let feed = FeedBuilder::default()
|
let feed = FeedBuilder::default()
|
||||||
.title(blog.title.clone())
|
.title(blog.title.clone())
|
||||||
.id(Instance::get_local(&*conn).unwrap().compute_box("~", name, "atom.xml"))
|
.id(Instance::get_local(&*conn).expect("blogs::atom_feed: local instance not found error")
|
||||||
|
.compute_box("~", name, "atom.xml"))
|
||||||
.entries(Post::get_recents_for_blog(&*conn, &blog, 15)
|
.entries(Post::get_recents_for_blog(&*conn, &blog, 15)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|p| super::post_to_atom(p, &*conn))
|
.map(|p| super::post_to_atom(p, &*conn))
|
||||||
.collect::<Vec<Entry>>())
|
.collect::<Vec<Entry>>())
|
||||||
.build()
|
.build()
|
||||||
.expect("Error building Atom feed");
|
.expect("blogs::atom_feed: feed creation error");
|
||||||
Content(ContentType::new("application", "atom+xml"), feed.to_string())
|
Some(Content(ContentType::new("application", "atom+xml"), feed.to_string()))
|
||||||
}
|
}
|
||||||
|
@ -28,9 +28,10 @@ struct NewCommentForm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[post("/~/<blog_name>/<slug>/comment", data = "<data>")]
|
#[post("/~/<blog_name>/<slug>/comment", data = "<data>")]
|
||||||
fn create(blog_name: String, slug: String, data: LenientForm<NewCommentForm>, user: User, conn: DbConn, worker: State<Pool<ThunkWorker<()>>>) -> Result<Redirect, Template> {
|
fn create(blog_name: String, slug: String, data: LenientForm<NewCommentForm>, user: User, conn: DbConn, worker: State<Pool<ThunkWorker<()>>>)
|
||||||
let blog = Blog::find_by_fqn(&*conn, blog_name.clone()).unwrap();
|
-> Result<Redirect, Option<Template>> {
|
||||||
let post = Post::find_by_slug(&*conn, slug.clone(), blog.id).unwrap();
|
let blog = Blog::find_by_fqn(&*conn, blog_name.clone()).ok_or(None)?;
|
||||||
|
let post = Post::find_by_slug(&*conn, slug.clone(), blog.id).ok_or(None)?;
|
||||||
let form = data.get();
|
let form = data.get();
|
||||||
form.validate()
|
form.validate()
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
@ -63,7 +64,7 @@ fn create(blog_name: String, slug: String, data: LenientForm<NewCommentForm>, us
|
|||||||
let comments = Comment::list_by_post(&*conn, post.id);
|
let comments = Comment::list_by_post(&*conn, post.id);
|
||||||
let comms = comments.clone();
|
let comms = comments.clone();
|
||||||
|
|
||||||
Template::render("posts/details", json!({
|
Some(Template::render("posts/details", json!({
|
||||||
"author": post.get_authors(&*conn)[0].to_json(&*conn),
|
"author": post.get_authors(&*conn)[0].to_json(&*conn),
|
||||||
"post": post,
|
"post": post,
|
||||||
"blog": blog,
|
"blog": blog,
|
||||||
@ -74,10 +75,10 @@ fn create(blog_name: String, slug: String, data: LenientForm<NewCommentForm>, us
|
|||||||
"has_reshared": user.has_reshared(&*conn, &post),
|
"has_reshared": user.has_reshared(&*conn, &post),
|
||||||
"account": user.to_json(&*conn),
|
"account": user.to_json(&*conn),
|
||||||
"date": &post.creation_date.timestamp(),
|
"date": &post.creation_date.timestamp(),
|
||||||
"previous": form.responding_to.map(|r| Comment::get(&*conn, r).expect("Error retrieving previous comment").to_json(&*conn, &vec![])),
|
"previous": form.responding_to.and_then(|r| Comment::get(&*conn, r)).map(|r| r.to_json(&*conn, &vec![])),
|
||||||
"user_fqn": user.get_fqn(&*conn),
|
"user_fqn": user.get_fqn(&*conn),
|
||||||
"errors": errors
|
"errors": errors
|
||||||
}))
|
})))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,21 +6,21 @@ use plume_models::users::User;
|
|||||||
|
|
||||||
#[catch(404)]
|
#[catch(404)]
|
||||||
fn not_found(req: &Request) -> Template {
|
fn not_found(req: &Request) -> Template {
|
||||||
let conn = req.guard::<DbConn>().expect("404: DbConn error");
|
let conn = req.guard::<DbConn>().succeeded();
|
||||||
let user = User::from_request(req).succeeded();
|
let user = User::from_request(req).succeeded();
|
||||||
Template::render("errors/404", json!({
|
Template::render("errors/404", json!({
|
||||||
"error_message": "Page not found",
|
"error_message": "Page not found",
|
||||||
"account": user.map(|u| u.to_json(&*conn))
|
"account": user.and_then(|u| conn.map(|conn| u.to_json(&*conn)))
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[catch(500)]
|
#[catch(500)]
|
||||||
fn server_error(req: &Request) -> Template {
|
fn server_error(req: &Request) -> Template {
|
||||||
let conn = req.guard::<DbConn>().expect("500: DbConn error");
|
let conn = req.guard::<DbConn>().succeeded();
|
||||||
let user = User::from_request(req).succeeded();
|
let user = User::from_request(req).succeeded();
|
||||||
Template::render("errors/500", json!({
|
Template::render("errors/500", json!({
|
||||||
"error_message": "Server error",
|
"error_message": "Server error",
|
||||||
"account": user.map(|u| u.to_json(&*conn))
|
"account": user.and_then(|u| conn.map(|conn| u.to_json(&*conn)))
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use rocket::{request::LenientForm, response::Redirect};
|
use rocket::{request::LenientForm, response::{status, Redirect}};
|
||||||
use rocket_contrib::{Json, Template};
|
use rocket_contrib::{Json, Template};
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use validator::{Validate};
|
use validator::{Validate};
|
||||||
@ -52,7 +52,7 @@ fn index(conn: DbConn, user: Option<User>) -> Template {
|
|||||||
|
|
||||||
#[get("/local?<page>")]
|
#[get("/local?<page>")]
|
||||||
fn paginated_local(conn: DbConn, user: Option<User>, page: Page) -> Template {
|
fn paginated_local(conn: DbConn, user: Option<User>, page: Page) -> Template {
|
||||||
let instance = Instance::get_local(&*conn).unwrap();
|
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());
|
let articles = Post::get_instance_page(&*conn, instance.id, page.limits());
|
||||||
Template::render("instance/local", json!({
|
Template::render("instance/local", json!({
|
||||||
"account": user.map(|u| u.to_json(&*conn)),
|
"account": user.map(|u| u.to_json(&*conn)),
|
||||||
@ -129,7 +129,7 @@ fn update_settings(conn: DbConn, admin: Admin, form: LenientForm<InstanceSetting
|
|||||||
let form = form.get();
|
let form = form.get();
|
||||||
form.validate()
|
form.validate()
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
let instance = Instance::get_local(&*conn).unwrap();
|
let instance = Instance::get_local(&*conn).expect("instance::update_settings: local instance not found error");
|
||||||
instance.update(&*conn,
|
instance.update(&*conn,
|
||||||
form.name.clone(),
|
form.name.clone(),
|
||||||
form.open_registrations,
|
form.open_registrations,
|
||||||
@ -196,31 +196,31 @@ fn ban(_admin: Admin, conn: DbConn, id: i32) -> Redirect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[post("/inbox", data = "<data>")]
|
#[post("/inbox", data = "<data>")]
|
||||||
fn shared_inbox(conn: DbConn, data: String, headers: Headers) -> String {
|
fn shared_inbox(conn: DbConn, data: String, headers: Headers) -> Result<String, status::BadRequest<&'static str>> {
|
||||||
let act: serde_json::Value = serde_json::from_str(&data[..]).unwrap();
|
let act: serde_json::Value = serde_json::from_str(&data[..]).expect("instance::shared_inbox: deserialization error");
|
||||||
|
|
||||||
let activity = act.clone();
|
let activity = act.clone();
|
||||||
let actor_id = activity["actor"].as_str()
|
let actor_id = activity["actor"].as_str()
|
||||||
.unwrap_or_else(|| activity["actor"]["id"].as_str().expect("No actor ID for incoming activity, blocks by panicking"));
|
.or_else(|| activity["actor"]["id"].as_str()).ok_or(status::BadRequest(Some("Missing actor id for activity")))?;
|
||||||
|
|
||||||
let actor = User::from_url(&conn, actor_id.to_owned()).unwrap();
|
let actor = User::from_url(&conn, actor_id.to_owned()).expect("instance::shared_inbox: user error");
|
||||||
if !verify_http_headers(&actor, headers.0.clone(), data).is_secure() &&
|
if !verify_http_headers(&actor, headers.0.clone(), data).is_secure() &&
|
||||||
!act.clone().verify(&actor) {
|
!act.clone().verify(&actor) {
|
||||||
println!("Rejected invalid activity supposedly from {}, with headers {:?}", actor.username, headers.0);
|
println!("Rejected invalid activity supposedly from {}, with headers {:?}", actor.username, headers.0);
|
||||||
return "invalid signature".to_owned();
|
return Err(status::BadRequest(Some("Invalid signature")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if Instance::is_blocked(&*conn, actor_id.to_string()) {
|
if Instance::is_blocked(&*conn, actor_id.to_string()) {
|
||||||
return String::new();
|
return Ok(String::new());
|
||||||
}
|
}
|
||||||
let instance = Instance::get_local(&*conn).unwrap();
|
let instance = Instance::get_local(&*conn).expect("instance::shared_inbox: local instance not found error");
|
||||||
match instance.received(&*conn, act) {
|
Ok(match instance.received(&*conn, act) {
|
||||||
Ok(_) => String::new(),
|
Ok(_) => String::new(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Shared inbox error: {}\n{}", e.as_fail(), e.backtrace());
|
println!("Shared inbox error: {}\n{}", e.as_fail(), e.backtrace());
|
||||||
format!("Error: {}", e.as_fail())
|
format!("Error: {}", e.as_fail())
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/nodeinfo")]
|
#[get("/nodeinfo")]
|
||||||
@ -263,7 +263,7 @@ fn about(user: Option<User>, conn: DbConn) -> Template {
|
|||||||
|
|
||||||
#[get("/manifest.json")]
|
#[get("/manifest.json")]
|
||||||
fn web_manifest(conn: DbConn) -> Json<serde_json::Value> {
|
fn web_manifest(conn: DbConn) -> Json<serde_json::Value> {
|
||||||
let instance = Instance::get_local(&*conn).unwrap();
|
let instance = Instance::get_local(&*conn).expect("instance::web_manifest: local instance not found error");
|
||||||
Json(json!({
|
Json(json!({
|
||||||
"name": &instance.name,
|
"name": &instance.name,
|
||||||
"description": &instance.short_description,
|
"description": &instance.short_description,
|
||||||
|
@ -12,9 +12,9 @@ use plume_models::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[post("/~/<blog>/<slug>/like")]
|
#[post("/~/<blog>/<slug>/like")]
|
||||||
fn create(blog: String, slug: String, user: User, conn: DbConn, worker: State<Pool<ThunkWorker<()>>>) -> Redirect {
|
fn create(blog: String, slug: String, user: User, conn: DbConn, worker: State<Pool<ThunkWorker<()>>>) -> Option<Redirect> {
|
||||||
let b = Blog::find_by_fqn(&*conn, blog.clone()).unwrap();
|
let b = Blog::find_by_fqn(&*conn, blog.clone())?;
|
||||||
let post = Post::find_by_slug(&*conn, slug.clone(), b.id).unwrap();
|
let post = Post::find_by_slug(&*conn, slug.clone(), b.id)?;
|
||||||
|
|
||||||
if !user.has_liked(&*conn, &post) {
|
if !user.has_liked(&*conn, &post) {
|
||||||
let like = likes::Like::insert(&*conn, likes::NewLike {
|
let like = likes::Like::insert(&*conn, likes::NewLike {
|
||||||
@ -29,13 +29,13 @@ fn create(blog: String, slug: String, user: User, conn: DbConn, worker: State<Po
|
|||||||
let act = like.into_activity(&*conn);
|
let act = like.into_activity(&*conn);
|
||||||
worker.execute(Thunk::of(move || broadcast(&user, act, dest)));
|
worker.execute(Thunk::of(move || broadcast(&user, act, dest)));
|
||||||
} else {
|
} else {
|
||||||
let like = likes::Like::find_by_user_on_post(&*conn, user.id, post.id).unwrap();
|
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 delete_act = like.delete(&*conn);
|
||||||
let dest = User::one_by_instance(&*conn);
|
let dest = User::one_by_instance(&*conn);
|
||||||
worker.execute(Thunk::of(move || broadcast(&user, delete_act, dest)));
|
worker.execute(Thunk::of(move || broadcast(&user, delete_act, dest)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Redirect::to(uri!(super::posts::details: blog = blog, slug = slug))
|
Some(Redirect::to(uri!(super::posts::details: blog = blog, slug = slug)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/~/<blog>/<slug>/like", rank = 2)]
|
#[post("/~/<blog>/<slug>/like", rank = 2)]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use guid_create::GUID;
|
use guid_create::GUID;
|
||||||
use multipart::server::{Multipart, save::{SavedData, SaveResult}};
|
use multipart::server::{Multipart, save::{SavedData, SaveResult}};
|
||||||
use rocket::{Data, http::ContentType, response::Redirect};
|
use rocket::{Data, http::ContentType, response::{Redirect, status}};
|
||||||
use rocket_contrib::Template;
|
use rocket_contrib::Template;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
@ -25,30 +25,27 @@ fn new(user: User, conn: DbConn) -> Template {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[post("/medias/new", data = "<data>")]
|
#[post("/medias/new", data = "<data>")]
|
||||||
fn upload(user: User, data: Data, ct: &ContentType, conn: DbConn) -> Redirect {
|
fn upload(user: User, data: Data, ct: &ContentType, conn: DbConn) -> Result<Redirect, status::BadRequest<&'static str>> {
|
||||||
if ct.is_form_data() {
|
if ct.is_form_data() {
|
||||||
let (_, boundary) = ct.params().find(|&(k, _)| k == "boundary").expect("No boundary");
|
let (_, boundary) = ct.params().find(|&(k, _)| k == "boundary").ok_or_else(|| status::BadRequest(Some("No boundary")))?;
|
||||||
|
|
||||||
match Multipart::with_body(data.open(), boundary).save().temp() {
|
match Multipart::with_body(data.open(), boundary).save().temp() {
|
||||||
SaveResult::Full(entries) => {
|
SaveResult::Full(entries) => {
|
||||||
let fields = entries.fields;
|
let fields = entries.fields;
|
||||||
|
|
||||||
let filename = fields.get(&"file".to_string()).unwrap().into_iter().next().unwrap().headers
|
let filename = fields.get(&"file".to_string()).and_then(|v| v.into_iter().next())
|
||||||
.filename.clone()
|
.ok_or_else(|| status::BadRequest(Some("No file uploaded")))?.headers
|
||||||
.unwrap_or("x.png".to_string()); // PNG by default
|
.filename.clone();
|
||||||
let ext = filename.rsplitn(2, ".")
|
let ext = filename.and_then(|f| f.rsplit('.').next().map(|ext| ext.to_owned()))
|
||||||
.next()
|
.unwrap_or("png".to_owned());
|
||||||
.unwrap();
|
|
||||||
let dest = format!("static/media/{}.{}", GUID::rand().to_string(), ext);
|
let dest = format!("static/media/{}.{}", GUID::rand().to_string(), ext);
|
||||||
|
|
||||||
if let SavedData::Bytes(ref bytes) = fields[&"file".to_string()][0].data {
|
match fields[&"file".to_string()][0].data {
|
||||||
fs::write(&dest, bytes).expect("Couldn't save upload");
|
SavedData::Bytes(ref bytes) => fs::write(&dest, bytes).expect("media::upload: Couldn't save upload"),
|
||||||
} else {
|
SavedData::File(ref path, _) => {fs::copy(path, &dest).expect("media::upload: Couldn't copy upload");},
|
||||||
if let SavedData::File(ref path, _) = fields[&"file".to_string()][0].data {
|
_ => {
|
||||||
fs::copy(path, &dest).expect("Couldn't copy temp upload");
|
println!("not a file");
|
||||||
} else {
|
return Ok(Redirect::to(uri!(new)));
|
||||||
println!("not file");
|
|
||||||
return Redirect::to(uri!(new));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,16 +64,16 @@ fn upload(user: User, data: Data, ct: &ContentType, conn: DbConn) -> Redirect {
|
|||||||
owner_id: user.id
|
owner_id: user.id
|
||||||
});
|
});
|
||||||
println!("ok");
|
println!("ok");
|
||||||
Redirect::to(uri!(details: id = media.id))
|
Ok(Redirect::to(uri!(details: id = media.id)))
|
||||||
},
|
},
|
||||||
SaveResult::Partial(_, _) | SaveResult::Error(_) => {
|
SaveResult::Partial(_, _) | SaveResult::Error(_) => {
|
||||||
println!("partial err");
|
println!("partial err");
|
||||||
Redirect::to(uri!(new))
|
Ok(Redirect::to(uri!(new)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
println!("not form data");
|
println!("not form data");
|
||||||
Redirect::to(uri!(new))
|
Ok(Redirect::to(uri!(new)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,15 +95,15 @@ fn details(id: i32, user: User, conn: DbConn) -> Template {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[post("/medias/<id>/delete")]
|
#[post("/medias/<id>/delete")]
|
||||||
fn delete(id: i32, _user: User, conn: DbConn) -> Redirect {
|
fn delete(id: i32, _user: User, conn: DbConn) -> Option<Redirect> {
|
||||||
let media = Media::get(&*conn, id).expect("Media to delete not found");
|
let media = Media::get(&*conn, id)?;
|
||||||
media.delete(&*conn);
|
media.delete(&*conn);
|
||||||
Redirect::to(uri!(list))
|
Some(Redirect::to(uri!(list)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/medias/<id>/avatar")]
|
#[post("/medias/<id>/avatar")]
|
||||||
fn set_avatar(id: i32, user: User, conn: DbConn) -> Redirect {
|
fn set_avatar(id: i32, user: User, conn: DbConn) -> Option<Redirect> {
|
||||||
let media = Media::get(&*conn, id).expect("Media to delete not found");
|
let media = Media::get(&*conn, id)?;
|
||||||
user.set_avatar(&*conn, media.id);
|
user.set_avatar(&*conn, media.id);
|
||||||
Redirect::to(uri!(details: id = id))
|
Some(Redirect::to(uri!(details: id = id)))
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,8 @@ fn details_response(blog: String, slug: String, conn: DbConn, user: Option<User>
|
|||||||
"has_reshared": user.clone().map(|u| u.has_reshared(&*conn, &post)).unwrap_or(false),
|
"has_reshared": user.clone().map(|u| u.has_reshared(&*conn, &post)).unwrap_or(false),
|
||||||
"account": &user.clone().map(|u| u.to_json(&*conn)),
|
"account": &user.clone().map(|u| u.to_json(&*conn)),
|
||||||
"date": &post.creation_date.timestamp(),
|
"date": &post.creation_date.timestamp(),
|
||||||
"previous": query.and_then(|q| q.responding_to.map(|r| Comment::get(&*conn, r).expect("Error retrieving previous comment").to_json(&*conn, &vec![]))),
|
"previous": query.and_then(|q| q.responding_to.map(|r| Comment::get(&*conn, r)
|
||||||
|
.expect("posts::details_reponse: Error retrieving previous comment").to_json(&*conn, &vec![]))),
|
||||||
"user_fqn": user.clone().map(|u| u.get_fqn(&*conn)).unwrap_or(String::new()),
|
"user_fqn": user.clone().map(|u| u.get_fqn(&*conn)).unwrap_or(String::new()),
|
||||||
"is_author": user.clone().map(|u| post.get_authors(&*conn).into_iter().any(|a| u.id == a.id)).unwrap_or(false),
|
"is_author": user.clone().map(|u| post.get_authors(&*conn).into_iter().any(|a| u.id == a.id)).unwrap_or(false),
|
||||||
"is_following": user.map(|u| u.is_following(&*conn, post.get_authors(&*conn)[0].id)).unwrap_or(false)
|
"is_following": user.map(|u| u.is_following(&*conn, post.get_authors(&*conn)[0].id)).unwrap_or(false)
|
||||||
@ -73,13 +74,13 @@ fn details_response(blog: String, slug: String, conn: DbConn, user: Option<User>
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[get("/~/<blog>/<slug>", rank = 3)]
|
#[get("/~/<blog>/<slug>", rank = 3)]
|
||||||
fn activity_details(blog: String, slug: String, conn: DbConn, _ap: ApRequest) -> Result<ActivityStream<Article>, String> {
|
fn activity_details(blog: String, slug: String, conn: DbConn, _ap: ApRequest) -> Result<ActivityStream<Article>, Option<String>> {
|
||||||
let blog = Blog::find_by_fqn(&*conn, blog).unwrap();
|
let blog = Blog::find_by_fqn(&*conn, blog).ok_or(None)?;
|
||||||
let post = Post::find_by_slug(&*conn, slug, blog.id).unwrap();
|
let post = Post::find_by_slug(&*conn, slug, blog.id).ok_or(None)?;
|
||||||
if post.published {
|
if post.published {
|
||||||
Ok(ActivityStream::new(post.into_activity(&*conn)))
|
Ok(ActivityStream::new(post.into_activity(&*conn)))
|
||||||
} else {
|
} else {
|
||||||
Err(String::from("Not published yet."))
|
Err(Some(String::from("Not published yet.")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,42 +93,42 @@ fn new_auth(blog: String) -> Flash<Redirect> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[get("/~/<blog>/new", rank = 1)]
|
#[get("/~/<blog>/new", rank = 1)]
|
||||||
fn new(blog: String, user: User, conn: DbConn) -> Template {
|
fn new(blog: String, user: User, conn: DbConn) -> Option<Template> {
|
||||||
let b = Blog::find_by_fqn(&*conn, blog.to_string()).unwrap();
|
let b = Blog::find_by_fqn(&*conn, blog.to_string())?;
|
||||||
|
|
||||||
if !user.is_author_in(&*conn, b.clone()) {
|
if !user.is_author_in(&*conn, b.clone()) {
|
||||||
Template::render("errors/403", json!({
|
Some(Template::render("errors/403", json!({// TODO actually return 403 error code
|
||||||
"error_message": "You are not author in this blog."
|
"error_message": "You are not author in this blog."
|
||||||
}))
|
})))
|
||||||
} else {
|
} else {
|
||||||
Template::render("posts/new", json!({
|
Some(Template::render("posts/new", json!({
|
||||||
"account": user.to_json(&*conn),
|
"account": user.to_json(&*conn),
|
||||||
"instance": Instance::get_local(&*conn),
|
"instance": Instance::get_local(&*conn),
|
||||||
"editing": false,
|
"editing": false,
|
||||||
"errors": null,
|
"errors": null,
|
||||||
"form": null,
|
"form": null,
|
||||||
"is_draft": true,
|
"is_draft": true,
|
||||||
}))
|
})))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/~/<blog>/<slug>/edit")]
|
#[get("/~/<blog>/<slug>/edit")]
|
||||||
fn edit(blog: String, slug: String, user: User, conn: DbConn) -> Template {
|
fn edit(blog: String, slug: String, user: User, conn: DbConn) -> Option<Template> {
|
||||||
let b = Blog::find_by_fqn(&*conn, blog.to_string());
|
let b = Blog::find_by_fqn(&*conn, blog.to_string())?;
|
||||||
let post = b.clone().and_then(|blog| Post::find_by_slug(&*conn, slug, blog.id)).expect("Post to edit not found");
|
let post = Post::find_by_slug(&*conn, slug, b.id)?;
|
||||||
|
|
||||||
if !user.is_author_in(&*conn, b.clone().unwrap()) {
|
if !user.is_author_in(&*conn, b) {
|
||||||
Template::render("errors/403", json!({
|
Some(Template::render("errors/403", json!({// TODO actually return 403 error code
|
||||||
"error_message": "You are not author in this blog."
|
"error_message": "You are not author in this blog."
|
||||||
}))
|
})))
|
||||||
} else {
|
} else {
|
||||||
let source = if post.source.clone().len() > 0 {
|
let source = if post.source.len() > 0 {
|
||||||
post.source.clone()
|
post.source
|
||||||
} else {
|
} else {
|
||||||
post.content.clone().get().clone() // fallback to HTML if the markdown was not stored
|
post.content.get().clone() // fallback to HTML if the markdown was not stored
|
||||||
};
|
};
|
||||||
|
|
||||||
Template::render("posts/new", json!({
|
Some(Template::render("posts/new", json!({
|
||||||
"account": user.to_json(&*conn),
|
"account": user.to_json(&*conn),
|
||||||
"instance": Instance::get_local(&*conn),
|
"instance": Instance::get_local(&*conn),
|
||||||
"editing": true,
|
"editing": true,
|
||||||
@ -145,14 +146,15 @@ fn edit(blog: String, slug: String, user: User, conn: DbConn) -> Template {
|
|||||||
draft: true,
|
draft: true,
|
||||||
},
|
},
|
||||||
"is_draft": !post.published
|
"is_draft": !post.published
|
||||||
}))
|
})))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/~/<blog>/<slug>/edit", data = "<data>")]
|
#[post("/~/<blog>/<slug>/edit", data = "<data>")]
|
||||||
fn update(blog: String, slug: String, user: User, conn: DbConn, data: LenientForm<NewPostForm>, worker: State<Pool<ThunkWorker<()>>>) -> Result<Redirect, Template> {
|
fn update(blog: String, slug: String, user: User, conn: DbConn, data: LenientForm<NewPostForm>, worker: State<Pool<ThunkWorker<()>>>)
|
||||||
let b = Blog::find_by_fqn(&*conn, blog.to_string());
|
-> Result<Redirect, Option<Template>> {
|
||||||
let mut post = b.clone().and_then(|blog| Post::find_by_slug(&*conn, slug.clone(), blog.id)).expect("Post to update not found");
|
let b = Blog::find_by_fqn(&*conn, blog.to_string()).ok_or(None)?;
|
||||||
|
let mut post = Post::find_by_slug(&*conn, slug.clone(), b.id).ok_or(None)?;
|
||||||
|
|
||||||
let form = data.get();
|
let form = data.get();
|
||||||
let new_slug = if !post.published {
|
let new_slug = if !post.published {
|
||||||
@ -167,7 +169,7 @@ fn update(blog: String, slug: String, user: User, conn: DbConn, data: LenientFor
|
|||||||
};
|
};
|
||||||
|
|
||||||
if new_slug != slug {
|
if new_slug != slug {
|
||||||
if let Some(_) = Post::find_by_slug(&*conn, new_slug.clone(), b.clone().unwrap().id) {
|
if let Some(_) = Post::find_by_slug(&*conn, new_slug.clone(), b.id) {
|
||||||
errors.add("title", ValidationError {
|
errors.add("title", ValidationError {
|
||||||
code: Cow::from("existing_slug"),
|
code: Cow::from("existing_slug"),
|
||||||
message: Some(Cow::from("A post with the same title already exists.")),
|
message: Some(Cow::from("A post with the same title already exists.")),
|
||||||
@ -177,7 +179,7 @@ fn update(blog: String, slug: String, user: User, conn: DbConn, data: LenientFor
|
|||||||
}
|
}
|
||||||
|
|
||||||
if errors.is_empty() {
|
if errors.is_empty() {
|
||||||
if !user.is_author_in(&*conn, b.clone().unwrap()) {
|
if !user.is_author_in(&*conn, b) {
|
||||||
// actually it's not "Ok"…
|
// actually it's not "Ok"…
|
||||||
Ok(Redirect::to(uri!(super::blogs::details: name = blog)))
|
Ok(Redirect::to(uri!(super::blogs::details: name = blog)))
|
||||||
} else {
|
} else {
|
||||||
@ -229,14 +231,14 @@ fn update(blog: String, slug: String, user: User, conn: DbConn, data: LenientFor
|
|||||||
Ok(Redirect::to(uri!(details: blog = blog, slug = new_slug)))
|
Ok(Redirect::to(uri!(details: blog = blog, slug = new_slug)))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(Template::render("posts/new", json!({
|
Err(Some(Template::render("posts/new", json!({
|
||||||
"account": user.to_json(&*conn),
|
"account": user.to_json(&*conn),
|
||||||
"instance": Instance::get_local(&*conn),
|
"instance": Instance::get_local(&*conn),
|
||||||
"editing": true,
|
"editing": true,
|
||||||
"errors": errors.inner(),
|
"errors": errors.inner(),
|
||||||
"form": form,
|
"form": form,
|
||||||
"is_draft": form.draft,
|
"is_draft": form.draft,
|
||||||
})))
|
}))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,8 +265,8 @@ fn valid_slug(title: &str) -> Result<(), ValidationError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[post("/~/<blog_name>/new", data = "<data>")]
|
#[post("/~/<blog_name>/new", data = "<data>")]
|
||||||
fn create(blog_name: String, data: LenientForm<NewPostForm>, user: User, conn: DbConn, worker: State<Pool<ThunkWorker<()>>>) -> Result<Redirect, Template> {
|
fn create(blog_name: String, data: LenientForm<NewPostForm>, user: User, conn: DbConn, worker: State<Pool<ThunkWorker<()>>>) -> Result<Redirect, Option<Template>> {
|
||||||
let blog = Blog::find_by_fqn(&*conn, blog_name.to_string()).unwrap();
|
let blog = Blog::find_by_fqn(&*conn, blog_name.to_string()).ok_or(None)?;
|
||||||
let form = data.get();
|
let form = data.get();
|
||||||
let slug = form.title.to_string().to_kebab_case();
|
let slug = form.title.to_string().to_kebab_case();
|
||||||
|
|
||||||
@ -331,14 +333,14 @@ fn create(blog_name: String, data: LenientForm<NewPostForm>, user: User, conn: D
|
|||||||
Ok(Redirect::to(uri!(details: blog = blog_name, slug = slug)))
|
Ok(Redirect::to(uri!(details: blog = blog_name, slug = slug)))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(Template::render("posts/new", json!({
|
Err(Some(Template::render("posts/new", json!({
|
||||||
"account": user.to_json(&*conn),
|
"account": user.to_json(&*conn),
|
||||||
"instance": Instance::get_local(&*conn),
|
"instance": Instance::get_local(&*conn),
|
||||||
"editing": false,
|
"editing": false,
|
||||||
"errors": errors.inner(),
|
"errors": errors.inner(),
|
||||||
"form": form,
|
"form": form,
|
||||||
"is_draft": form.draft
|
"is_draft": form.draft
|
||||||
})))
|
}))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,9 +12,9 @@ use plume_models::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[post("/~/<blog>/<slug>/reshare")]
|
#[post("/~/<blog>/<slug>/reshare")]
|
||||||
fn create(blog: String, slug: String, user: User, conn: DbConn, worker: State<Pool<ThunkWorker<()>>>) -> Redirect {
|
fn create(blog: String, slug: String, user: User, conn: DbConn, worker: State<Pool<ThunkWorker<()>>>) -> Option<Redirect> {
|
||||||
let b = Blog::find_by_fqn(&*conn, blog.clone()).unwrap();
|
let b = Blog::find_by_fqn(&*conn, blog.clone())?;
|
||||||
let post = Post::find_by_slug(&*conn, slug.clone(), b.id).unwrap();
|
let post = Post::find_by_slug(&*conn, slug.clone(), b.id)?;
|
||||||
|
|
||||||
if !user.has_reshared(&*conn, &post) {
|
if !user.has_reshared(&*conn, &post) {
|
||||||
let reshare = Reshare::insert(&*conn, NewReshare {
|
let reshare = Reshare::insert(&*conn, NewReshare {
|
||||||
@ -29,13 +29,14 @@ fn create(blog: String, slug: String, user: User, conn: DbConn, worker: State<Po
|
|||||||
let act = reshare.into_activity(&*conn);
|
let act = reshare.into_activity(&*conn);
|
||||||
worker.execute(Thunk::of(move || broadcast(&user, act, dest)));
|
worker.execute(Thunk::of(move || broadcast(&user, act, dest)));
|
||||||
} else {
|
} else {
|
||||||
let reshare = Reshare::find_by_user_on_post(&*conn, user.id, post.id).unwrap();
|
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 delete_act = reshare.delete(&*conn);
|
||||||
let dest = User::one_by_instance(&*conn);
|
let dest = User::one_by_instance(&*conn);
|
||||||
worker.execute(Thunk::of(move || broadcast(&user, delete_act, dest)));
|
worker.execute(Thunk::of(move || broadcast(&user, delete_act, dest)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Redirect::to(uri!(super::posts::details: blog = blog, slug = slug))
|
Some(Redirect::to(uri!(super::posts::details: blog = blog, slug = slug)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/~/<blog>/<slug>/reshare", rank=1)]
|
#[post("/~/<blog>/<slug>/reshare", rank=1)]
|
||||||
|
@ -50,22 +50,23 @@ struct LoginForm {
|
|||||||
fn create(conn: DbConn, data: LenientForm<LoginForm>, flash: Option<FlashMessage>, mut cookies: Cookies) -> Result<Redirect, Template> {
|
fn create(conn: DbConn, data: LenientForm<LoginForm>, flash: Option<FlashMessage>, mut cookies: Cookies) -> Result<Redirect, Template> {
|
||||||
let form = data.get();
|
let form = data.get();
|
||||||
let user = User::find_by_email(&*conn, form.email_or_name.to_string())
|
let user = User::find_by_email(&*conn, form.email_or_name.to_string())
|
||||||
.map(|u| Ok(u))
|
.or_else(|| User::find_local(&*conn, form.email_or_name.to_string()));
|
||||||
.unwrap_or_else(|| User::find_local(&*conn, form.email_or_name.to_string()).map(|u| Ok(u)).unwrap_or(Err(())));
|
|
||||||
|
|
||||||
let mut errors = match form.validate() {
|
let mut errors = match form.validate() {
|
||||||
Ok(_) => ValidationErrors::new(),
|
Ok(_) => ValidationErrors::new(),
|
||||||
Err(e) => e
|
Err(e) => e
|
||||||
};
|
};
|
||||||
if let Err(_) = user.clone() {
|
|
||||||
|
if let Some(user) = user.clone() {
|
||||||
|
if !user.auth(form.password.clone()) {
|
||||||
|
let mut err = ValidationError::new("invalid_login");
|
||||||
|
err.message = Some(Cow::from("Invalid username or password"));
|
||||||
|
errors.add("email_or_name", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// Fake password verification, only to avoid different login times
|
// Fake password verification, only to avoid different login times
|
||||||
// that could be used to see if an email adress is registered or not
|
// that could be used to see if an email adress is registered or not
|
||||||
User::get(&*conn, 1).map(|u| u.auth(form.password.clone()));
|
User::get(&*conn, 1).map(|u| u.auth(form.password.clone()));
|
||||||
|
|
||||||
let mut err = ValidationError::new("invalid_login");
|
|
||||||
err.message = Some(Cow::from("Invalid username or password"));
|
|
||||||
errors.add("email_or_name", err)
|
|
||||||
} else if !user.clone().expect("User not found").auth(form.password.clone()) {
|
|
||||||
let mut err = ValidationError::new("invalid_login");
|
let mut err = ValidationError::new("invalid_login");
|
||||||
err.message = Some(Cow::from("Invalid username or password"));
|
err.message = Some(Cow::from("Invalid username or password"));
|
||||||
errors.add("email_or_name", err)
|
errors.add("email_or_name", err)
|
||||||
@ -107,7 +108,6 @@ fn create(conn: DbConn, data: LenientForm<LoginForm>, flash: Option<FlashMessage
|
|||||||
|
|
||||||
#[get("/logout")]
|
#[get("/logout")]
|
||||||
fn delete(mut cookies: Cookies) -> Redirect {
|
fn delete(mut cookies: Cookies) -> Redirect {
|
||||||
let cookie = cookies.get_private(AUTH_COOKIE).unwrap();
|
cookies.get_private(AUTH_COOKIE).map(|cookie| cookies.remove_private(cookie));
|
||||||
cookies.remove_private(cookie);
|
|
||||||
Redirect::to("/")
|
Redirect::to("/")
|
||||||
}
|
}
|
||||||
|
@ -10,19 +10,19 @@ use plume_models::{
|
|||||||
use routes::Page;
|
use routes::Page;
|
||||||
|
|
||||||
#[get("/tag/<name>")]
|
#[get("/tag/<name>")]
|
||||||
fn tag(user: Option<User>, conn: DbConn, name: String) -> Template {
|
fn tag(user: Option<User>, conn: DbConn, name: String) -> Option<Template> {
|
||||||
paginated_tag(user, conn, name, Page::first())
|
paginated_tag(user, conn, name, Page::first())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/tag/<name>?<page>")]
|
#[get("/tag/<name>?<page>")]
|
||||||
fn paginated_tag(user: Option<User>, conn: DbConn, name: String, page: Page) -> Template {
|
fn paginated_tag(user: Option<User>, conn: DbConn, name: String, page: Page) -> Option<Template> {
|
||||||
let tag = Tag::find_by_name(&*conn, name).expect("Rendering tags::tag: tag not found");
|
let tag = Tag::find_by_name(&*conn, name)?;
|
||||||
let posts = Post::list_by_tag(&*conn, tag.tag.clone(), page.limits());
|
let posts = Post::list_by_tag(&*conn, tag.tag.clone(), page.limits());
|
||||||
Template::render("tags/index", json!({
|
Some(Template::render("tags/index", json!({
|
||||||
"tag": tag.clone(),
|
"tag": tag.clone(),
|
||||||
"account": user.map(|u| u.to_json(&*conn)),
|
"account": user.map(|u| u.to_json(&*conn)),
|
||||||
"articles": posts.into_iter().map(|p| p.to_json(&*conn)).collect::<Vec<serde_json::Value>>(),
|
"articles": posts.into_iter().map(|p| p.to_json(&*conn)).collect::<Vec<serde_json::Value>>(),
|
||||||
"page": page.page,
|
"page": page.page,
|
||||||
"n_pages": Page::total(Post::count_for_tag(&*conn, tag.tag) as i32)
|
"n_pages": Page::total(Post::count_for_tag(&*conn, tag.tag) as i32)
|
||||||
}))
|
})))
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ use activitypub::{
|
|||||||
use atom_syndication::{Entry, FeedBuilder};
|
use atom_syndication::{Entry, FeedBuilder};
|
||||||
use rocket::{
|
use rocket::{
|
||||||
request::LenientForm,
|
request::LenientForm,
|
||||||
response::{Redirect, Flash, Content},
|
response::{Content, Flash, Redirect, status},
|
||||||
http::{ContentType, Cookies}
|
http::{ContentType, Cookies}
|
||||||
};
|
};
|
||||||
use rocket_contrib::Template;
|
use rocket_contrib::Template;
|
||||||
@ -70,7 +70,7 @@ fn details(name: String, conn: DbConn, account: Option<User>, worker: Worker, fe
|
|||||||
worker.execute(Thunk::of(move || {
|
worker.execute(Thunk::of(move || {
|
||||||
for user_id in user_clone.fetch_followers_ids() {
|
for user_id in user_clone.fetch_followers_ids() {
|
||||||
let follower = User::find_by_ap_url(&*fecth_followers_conn, user_id.clone())
|
let follower = User::find_by_ap_url(&*fecth_followers_conn, user_id.clone())
|
||||||
.unwrap_or_else(|| User::fetch_from_url(&*fecth_followers_conn, user_id).expect("Couldn't fetch follower"));
|
.unwrap_or_else(|| User::fetch_from_url(&*fecth_followers_conn, user_id).expect("user::details: Couldn't fetch follower"));
|
||||||
follows::Follow::insert(&*fecth_followers_conn, follows::NewFollow {
|
follows::Follow::insert(&*fecth_followers_conn, follows::NewFollow {
|
||||||
follower_id: follower.id,
|
follower_id: follower.id,
|
||||||
following_id: user_clone.id,
|
following_id: user_clone.id,
|
||||||
@ -121,8 +121,8 @@ fn dashboard_auth() -> Flash<Redirect> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[post("/@/<name>/follow")]
|
#[post("/@/<name>/follow")]
|
||||||
fn follow(name: String, conn: DbConn, user: User, worker: Worker) -> Redirect {
|
fn follow(name: String, conn: DbConn, user: User, worker: Worker) -> Option<Redirect> {
|
||||||
let target = User::find_by_fqn(&*conn, name.clone()).unwrap();
|
let target = User::find_by_fqn(&*conn, name.clone())?;
|
||||||
if let Some(follow) = follows::Follow::find(&*conn, user.id, target.id) {
|
if let Some(follow) = follows::Follow::find(&*conn, user.id, target.id) {
|
||||||
let delete_act = follow.delete(&*conn);
|
let delete_act = follow.delete(&*conn);
|
||||||
worker.execute(Thunk::of(move || broadcast(&user, delete_act, vec![target])));
|
worker.execute(Thunk::of(move || broadcast(&user, delete_act, vec![target])));
|
||||||
@ -137,7 +137,7 @@ fn follow(name: String, conn: DbConn, user: User, worker: Worker) -> Redirect {
|
|||||||
let act = f.into_activity(&*conn);
|
let act = f.into_activity(&*conn);
|
||||||
worker.execute(Thunk::of(move || broadcast(&user, act, vec![target])));
|
worker.execute(Thunk::of(move || broadcast(&user, act, vec![target])));
|
||||||
}
|
}
|
||||||
Redirect::to(uri!(details: name = name))
|
Some(Redirect::to(uri!(details: name = name)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/@/<name>/follow", rank = 2)]
|
#[post("/@/<name>/follow", rank = 2)]
|
||||||
@ -176,9 +176,9 @@ fn followers(name: String, conn: DbConn, account: Option<User>) -> Template {
|
|||||||
|
|
||||||
|
|
||||||
#[get("/@/<name>", rank = 1)]
|
#[get("/@/<name>", rank = 1)]
|
||||||
fn activity_details(name: String, conn: DbConn, _ap: ApRequest) -> ActivityStream<CustomPerson> {
|
fn activity_details(name: String, conn: DbConn, _ap: ApRequest) -> Option<ActivityStream<CustomPerson>> {
|
||||||
let user = User::find_local(&*conn, name).unwrap();
|
let user = User::find_local(&*conn, name)?;
|
||||||
ActivityStream::new(user.into_activity(&*conn))
|
Some(ActivityStream::new(user.into_activity(&*conn)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/users/new")]
|
#[get("/users/new")]
|
||||||
@ -228,17 +228,16 @@ fn update(_name: String, conn: DbConn, user: User, data: LenientForm<UpdateUserF
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[post("/@/<name>/delete")]
|
#[post("/@/<name>/delete")]
|
||||||
fn delete(name: String, conn: DbConn, user: User, mut cookies: Cookies) -> Redirect {
|
fn delete(name: String, conn: DbConn, user: User, mut cookies: Cookies) -> Option<Redirect> {
|
||||||
let account = User::find_by_fqn(&*conn, name.clone()).unwrap();
|
let account = User::find_by_fqn(&*conn, name.clone())?;
|
||||||
if user.id == account.id {
|
if user.id == account.id {
|
||||||
account.delete(&*conn);
|
account.delete(&*conn);
|
||||||
|
|
||||||
let cookie = cookies.get_private(AUTH_COOKIE).unwrap();
|
cookies.get_private(AUTH_COOKIE).map(|cookie| cookies.remove_private(cookie));
|
||||||
cookies.remove_private(cookie);
|
|
||||||
|
|
||||||
Redirect::to(uri!(super::instance::index))
|
Some(Redirect::to(uri!(super::instance::index)))
|
||||||
} else {
|
} else {
|
||||||
Redirect::to(uri!(edit: name = name))
|
Some(Redirect::to(uri!(edit: name = name)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,54 +290,54 @@ fn create(conn: DbConn, data: LenientForm<NewUserForm>) -> Result<Redirect, Temp
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[get("/@/<name>/outbox")]
|
#[get("/@/<name>/outbox")]
|
||||||
fn outbox(name: String, conn: DbConn) -> ActivityStream<OrderedCollection> {
|
fn outbox(name: String, conn: DbConn) -> Option<ActivityStream<OrderedCollection>> {
|
||||||
let user = User::find_local(&*conn, name).unwrap();
|
let user = User::find_local(&*conn, name)?;
|
||||||
user.outbox(&*conn)
|
Some(user.outbox(&*conn))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/@/<name>/inbox", data = "<data>")]
|
#[post("/@/<name>/inbox", data = "<data>")]
|
||||||
fn inbox(name: String, conn: DbConn, data: String, headers: Headers) -> String {
|
fn inbox(name: String, conn: DbConn, data: String, headers: Headers) -> Result<String, Option<status::BadRequest<&'static str>>> {
|
||||||
let user = User::find_local(&*conn, name).unwrap();
|
let user = User::find_local(&*conn, name).ok_or(None)?;
|
||||||
let act: serde_json::Value = serde_json::from_str(&data[..]).unwrap();
|
let act: serde_json::Value = serde_json::from_str(&data[..]).expect("user::inbox: deserialization error");
|
||||||
|
|
||||||
let activity = act.clone();
|
let activity = act.clone();
|
||||||
let actor_id = activity["actor"].as_str()
|
let actor_id = activity["actor"].as_str()
|
||||||
.unwrap_or_else(|| activity["actor"]["id"].as_str().expect("User: No actor ID for incoming activity, blocks by panicking"));
|
.or_else(|| activity["actor"]["id"].as_str()).ok_or(Some(status::BadRequest(Some("Missing actor id for activity"))))?;
|
||||||
|
|
||||||
let actor = User::from_url(&conn, actor_id.to_owned()).unwrap();
|
let actor = User::from_url(&conn, actor_id.to_owned()).expect("user::inbox: user error");
|
||||||
if !verify_http_headers(&actor, headers.0.clone(), data).is_secure() &&
|
if !verify_http_headers(&actor, headers.0.clone(), data).is_secure() &&
|
||||||
!act.clone().verify(&actor) {
|
!act.clone().verify(&actor) {
|
||||||
println!("Rejected invalid activity supposedly from {}, with headers {:?}", actor.username, headers.0);
|
println!("Rejected invalid activity supposedly from {}, with headers {:?}", actor.username, headers.0);
|
||||||
return "invalid signature".to_owned();
|
return Err(Some(status::BadRequest(Some("Invalid signature"))));
|
||||||
}
|
}
|
||||||
|
|
||||||
if Instance::is_blocked(&*conn, actor_id.to_string()) {
|
if Instance::is_blocked(&*conn, actor_id.to_string()) {
|
||||||
return String::new();
|
return Ok(String::new());
|
||||||
}
|
}
|
||||||
match user.received(&*conn, act) {
|
Ok(match user.received(&*conn, act) {
|
||||||
Ok(_) => String::new(),
|
Ok(_) => String::new(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("User inbox error: {}\n{}", e.as_fail(), e.backtrace());
|
println!("User inbox error: {}\n{}", e.as_fail(), e.backtrace());
|
||||||
format!("Error: {}", e.as_fail())
|
format!("Error: {}", e.as_fail())
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/@/<name>/followers")]
|
#[get("/@/<name>/followers")]
|
||||||
fn ap_followers(name: String, conn: DbConn, _ap: ApRequest) -> ActivityStream<OrderedCollection> {
|
fn ap_followers(name: String, conn: DbConn, _ap: ApRequest) -> Option<ActivityStream<OrderedCollection>> {
|
||||||
let user = User::find_local(&*conn, name).unwrap();
|
let user = User::find_local(&*conn, name)?;
|
||||||
let followers = user.get_followers(&*conn).into_iter().map(|f| Id::new(f.ap_url)).collect::<Vec<Id>>();
|
let followers = user.get_followers(&*conn).into_iter().map(|f| Id::new(f.ap_url)).collect::<Vec<Id>>();
|
||||||
|
|
||||||
let mut coll = OrderedCollection::default();
|
let mut coll = OrderedCollection::default();
|
||||||
coll.object_props.set_id_string(user.followers_endpoint).expect("Follower collection: id error");
|
coll.object_props.set_id_string(user.followers_endpoint).expect("user::ap_followers: id error");
|
||||||
coll.collection_props.set_total_items_u64(followers.len() as u64).expect("Follower collection: totalItems error");
|
coll.collection_props.set_total_items_u64(followers.len() as u64).expect("user::ap_followers: totalItems error");
|
||||||
coll.collection_props.set_items_link_vec(followers).expect("Follower collection: items error");
|
coll.collection_props.set_items_link_vec(followers).expect("user::ap_followers items error");
|
||||||
ActivityStream::new(coll)
|
Some(ActivityStream::new(coll))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/@/<name>/atom.xml")]
|
#[get("/@/<name>/atom.xml")]
|
||||||
fn atom_feed(name: String, conn: DbConn) -> Content<String> {
|
fn atom_feed(name: String, conn: DbConn) -> Option<Content<String>> {
|
||||||
let author = User::find_by_fqn(&*conn, name.clone()).expect("Unable to find author");
|
let author = User::find_by_fqn(&*conn, name.clone())?;
|
||||||
let feed = FeedBuilder::default()
|
let feed = FeedBuilder::default()
|
||||||
.title(author.display_name.clone())
|
.title(author.display_name.clone())
|
||||||
.id(Instance::get_local(&*conn).unwrap().compute_box("~", name, "atom.xml"))
|
.id(Instance::get_local(&*conn).unwrap().compute_box("~", name, "atom.xml"))
|
||||||
@ -347,6 +346,6 @@ fn atom_feed(name: String, conn: DbConn) -> Content<String> {
|
|||||||
.map(|p| super::post_to_atom(p, &*conn))
|
.map(|p| super::post_to_atom(p, &*conn))
|
||||||
.collect::<Vec<Entry>>())
|
.collect::<Vec<Entry>>())
|
||||||
.build()
|
.build()
|
||||||
.expect("Error building Atom feed");
|
.expect("user::atom_feed: Error building Atom feed");
|
||||||
Content(ContentType::new("application", "atom+xml"), feed.to_string())
|
Some(Content(ContentType::new("application", "atom+xml"), feed.to_string()))
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user