Remove Canapi (#540)

* Remove Canapi

It added more complexity than it helped.

* Fail if there are many blog, but none was specified

* cargo fmt
This commit is contained in:
Baptiste Gelez
2019-04-28 22:17:21 +01:00
committed by GitHub
parent 787eb7f399
commit ec57f1e687
14 changed files with 298 additions and 435 deletions
+19 -7
View File
@@ -1,12 +1,24 @@
use canapi::Provider;
use rocket_contrib::json::Json;
use serde_json;
use plume_api::apps::AppEndpoint;
use plume_models::{apps::App, db_conn::DbConn, Connection};
use crate::api::Api;
use plume_api::apps::NewAppData;
use plume_common::utils::random_hex;
use plume_models::{apps::*, db_conn::DbConn};
#[post("/apps", data = "<data>")]
pub fn create(conn: DbConn, data: Json<AppEndpoint>) -> Json<serde_json::Value> {
let post = <App as Provider<Connection>>::create(&*conn, (*data).clone()).ok();
Json(json!(post))
pub fn create(conn: DbConn, data: Json<NewAppData>) -> Api<App> {
let client_id = random_hex();
let client_secret = random_hex();
let app = App::insert(
&*conn,
NewApp {
name: data.name.clone(),
client_id,
client_secret,
redirect_uri: data.redirect_uri.clone(),
website: data.website.clone(),
},
)?;
Ok(Json(app))
}
+8
View File
@@ -9,6 +9,8 @@ use serde_json;
use plume_common::utils::random_hex;
use plume_models::{api_tokens::*, apps::App, users::User, Error, PlumeRocket};
type Api<T> = Result<Json<T>, ApiError>;
#[derive(Debug)]
pub struct ApiError(Error);
@@ -18,6 +20,12 @@ impl From<Error> for ApiError {
}
}
impl From<std::option::NoneError> for ApiError {
fn from(err: std::option::NoneError) -> ApiError {
ApiError(err.into())
}
}
impl<'r> Responder<'r> for ApiError {
fn respond_to(self, req: &Request) -> response::Result<'r> {
match self.0 {
+221 -39
View File
@@ -1,54 +1,236 @@
use canapi::{Error as ApiError, Provider};
use rocket::http::uri::Origin;
use chrono::NaiveDateTime;
use heck::{CamelCase, KebabCase};
use rocket_contrib::json::Json;
use serde_json;
use serde_qs;
use api::authorization::*;
use plume_api::posts::PostEndpoint;
use plume_models::{posts::Post, users::User, PlumeRocket};
use crate::api::{authorization::*, Api};
use plume_api::posts::*;
use plume_common::{activity_pub::broadcast, utils::md_to_html};
use plume_models::{
blogs::Blog, db_conn::DbConn, instance::Instance, medias::Media, mentions::*, post_authors::*,
posts::*, safe_string::SafeString, tags::*, users::User, Error, PlumeRocket,
};
#[get("/posts/<id>")]
pub fn get(
id: i32,
auth: Option<Authorization<Read, Post>>,
mut rockets: PlumeRocket,
) -> Json<serde_json::Value> {
rockets.user = auth.and_then(|a| User::get(&*rockets.conn, a.0.user_id).ok());
let post = <Post as Provider<PlumeRocket>>::get(&rockets, id).ok();
Json(json!(post))
pub fn get(id: i32, auth: Option<Authorization<Read, Post>>, conn: DbConn) -> Api<PostData> {
let user = auth.and_then(|a| User::get(&conn, a.0.user_id).ok());
let post = Post::get(&conn, id)?;
if !post.published
&& !user
.and_then(|u| post.is_author(&conn, u.id).ok())
.unwrap_or(false)
{
return Err(Error::Unauthorized.into());
}
Ok(Json(PostData {
authors: post
.get_authors(&conn)?
.into_iter()
.map(|a| a.username)
.collect(),
creation_date: post.creation_date.format("%Y-%m-%d").to_string(),
tags: Tag::for_post(&conn, post.id)?
.into_iter()
.map(|t| t.tag)
.collect(),
id: post.id,
title: post.title,
subtitle: post.subtitle,
content: post.content.to_string(),
source: Some(post.source),
blog_id: post.blog_id,
published: post.published,
license: post.license,
cover_id: post.cover_id,
}))
}
#[get("/posts")]
#[get("/posts?<title>&<subtitle>&<content>")]
pub fn list(
uri: &Origin,
title: Option<String>,
subtitle: Option<String>,
content: Option<String>,
auth: Option<Authorization<Read, Post>>,
mut rockets: PlumeRocket,
) -> Json<serde_json::Value> {
rockets.user = auth.and_then(|a| User::get(&*rockets.conn, a.0.user_id).ok());
let query: PostEndpoint =
serde_qs::from_str(uri.query().unwrap_or("")).expect("api::list: invalid query error");
let post = <Post as Provider<PlumeRocket>>::list(&rockets, query);
Json(json!(post))
conn: DbConn,
) -> Api<Vec<PostData>> {
let user = auth.and_then(|a| User::get(&conn, a.0.user_id).ok());
let user_id = user.map(|u| u.id);
Ok(Json(
Post::list_filtered(&conn, title, subtitle, content)?
.into_iter()
.filter(|p| {
p.published
|| user_id
.and_then(|u| p.is_author(&conn, u).ok())
.unwrap_or(false)
})
.filter_map(|p| {
Some(PostData {
authors: p
.get_authors(&conn)
.ok()?
.into_iter()
.map(|a| a.username)
.collect(),
creation_date: p.creation_date.format("%Y-%m-%d").to_string(),
tags: Tag::for_post(&conn, p.id)
.ok()?
.into_iter()
.map(|t| t.tag)
.collect(),
id: p.id,
title: p.title,
subtitle: p.subtitle,
content: p.content.to_string(),
source: Some(p.source),
blog_id: p.blog_id,
published: p.published,
license: p.license,
cover_id: p.cover_id,
})
})
.collect(),
))
}
#[post("/posts", data = "<payload>")]
pub fn create(
auth: Authorization<Write, Post>,
payload: Json<PostEndpoint>,
mut rockets: PlumeRocket,
) -> Json<serde_json::Value> {
rockets.user = User::get(&*rockets.conn, auth.0.user_id).ok();
let new_post = <Post as Provider<PlumeRocket>>::create(&rockets, (*payload).clone());
Json(new_post.map(|p| json!(p)).unwrap_or_else(|e| {
json!({
"error": "Invalid data, couldn't create new post",
"details": match e {
ApiError::Fetch(msg) => msg,
ApiError::SerDe(msg) => msg,
ApiError::NotFound(msg) => msg,
ApiError::Authorization(msg) => msg,
}
})
payload: Json<NewPostData>,
rockets: PlumeRocket,
) -> Api<PostData> {
let conn = &*rockets.conn;
let search = &rockets.searcher;
let worker = &rockets.worker;
let author = User::get(conn, auth.0.user_id)?;
let slug = &payload.title.clone().to_kebab_case();
let date = payload.creation_date.clone().and_then(|d| {
NaiveDateTime::parse_from_str(format!("{} 00:00:00", d).as_ref(), "%Y-%m-%d %H:%M:%S").ok()
});
let domain = &Instance::get_local(conn)?.public_domain;
let (content, mentions, hashtags) = md_to_html(
&payload.source,
domain,
false,
Some(Media::get_media_processor(conn, vec![&author])),
);
let blog = payload.blog_id.or_else(|| {
let blogs = Blog::find_for_author(conn, &author).ok()?;
if blogs.len() == 1 {
Some(blogs[0].id)
} else {
None
}
})?;
if Post::find_by_slug(conn, slug, blog).is_ok() {
return Err(Error::InvalidValue.into());
}
let post = Post::insert(
conn,
NewPost {
blog_id: blog,
slug: slug.to_string(),
title: payload.title.clone(),
content: SafeString::new(content.as_ref()),
published: payload.published.unwrap_or(true),
license: payload.license.clone().unwrap_or_else(|| {
Instance::get_local(conn)
.map(|i| i.default_license)
.unwrap_or_else(|_| String::from("CC-BY-SA"))
}),
creation_date: date,
ap_url: String::new(),
subtitle: payload.subtitle.clone().unwrap_or_default(),
source: payload.source.clone(),
cover_id: payload.cover_id,
},
search,
)?;
PostAuthor::insert(
conn,
NewPostAuthor {
author_id: author.id,
post_id: post.id,
},
)?;
if let Some(ref tags) = payload.tags {
for tag in tags {
Tag::insert(
conn,
NewTag {
tag: tag.to_string(),
is_hashtag: false,
post_id: post.id,
},
)?;
}
}
for hashtag in hashtags {
Tag::insert(
conn,
NewTag {
tag: hashtag.to_camel_case(),
is_hashtag: true,
post_id: post.id,
},
)?;
}
if post.published {
for m in mentions.into_iter() {
Mention::from_activity(
&*conn,
&Mention::build_activity(&rockets, &m)?,
post.id,
true,
true,
)?;
}
let act = post.create_activity(&*conn)?;
let dest = User::one_by_instance(&*conn)?;
worker.execute(move || broadcast(&author, act, dest));
}
Ok(Json(PostData {
authors: post.get_authors(conn)?.into_iter().map(|a| a.fqn).collect(),
creation_date: post.creation_date.format("%Y-%m-%d").to_string(),
tags: Tag::for_post(conn, post.id)?
.into_iter()
.map(|t| t.tag)
.collect(),
id: post.id,
title: post.title,
subtitle: post.subtitle,
content: post.content.to_string(),
source: Some(post.source),
blog_id: post.blog_id,
published: post.published,
license: post.license,
cover_id: post.cover_id,
}))
}
#[delete("/posts/<id>")]
pub fn delete(auth: Authorization<Write, Post>, rockets: PlumeRocket, id: i32) -> Api<()> {
let author = User::get(&*rockets.conn, auth.0.user_id)?;
if let Ok(post) = Post::get(&*rockets.conn, id) {
if post.is_author(&*rockets.conn, author.id).unwrap_or(false) {
post.delete(&*rockets.conn, &rockets.searcher)?;
}
}
Ok(Json(()))
}