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:
Baptiste Gelez
2018-12-29 09:36:07 +01:00
committed by GitHub
parent 4059a840be
commit 80a4dae8bd
50 changed files with 1879 additions and 1990 deletions
+87 -95
View File
@@ -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"
}
]
}))
})))
}