diff --git a/Cargo.lock b/Cargo.lock index 734c5917..ee275d22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -236,6 +236,14 @@ dependencies = [ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "canapi" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.77 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cc" version = "1.0.24" @@ -1478,6 +1486,15 @@ dependencies = [ "workerpool 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "plume-api" +version = "0.1.0" +dependencies = [ + "canapi 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.77 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.77 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "plume-common" version = "0.2.0" @@ -1510,11 +1527,13 @@ dependencies = [ "activitypub 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "ammonia 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "bcrypt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "canapi 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "diesel 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "heck 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "openssl 0.10.11 (registry+https://github.com/rust-lang/crates.io-index)", + "plume-api 0.1.0", "plume-common 0.2.0", "reqwest 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)", "rocket 0.4.0-dev (git+https://github.com/SergioBenitez/Rocket?rev=55459db7732b9a240826a5c120c650f87e3372ce)", @@ -2745,6 +2764,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" "checksum byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "90492c5858dd7d2e78691cfb89f90d273a2800fc11d98f60786e5d87e2f83781" "checksum bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0ce55bd354b095246fc34caf4e9e242f5297a7fd938b090cadfea6eee614aa62" +"checksum canapi 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff3e02a04f44b531d851d2db62f95aabf65d033a6724767a4bed9732563e9bc4" "checksum cc 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "70f2a88c2e69ceee91c209d8ef25b81fc1a65f42c7f14dfd59d1fed189e514d1" "checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3" "checksum chomp 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9f74ad218e66339b11fd23f693fb8f1d621e80ba6ac218297be26073365d163d" diff --git a/Cargo.toml b/Cargo.toml index 6ce996c2..78e9f70e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,4 +58,4 @@ git = "https://github.com/BaptisteGelez/rocket_i18n" rev = "75a3bfd7b847324c078a355a7f101f8241a9f59b" [workspace] -members = ["plume-models", "plume-common"] +members = ["plume-api", "plume-models", "plume-common"] diff --git a/docs/API.md b/docs/API.md new file mode 100644 index 00000000..a0c064ac --- /dev/null +++ b/docs/API.md @@ -0,0 +1,15 @@ + + +
+ + diff --git a/docs/api.yaml b/docs/api.yaml new file mode 100644 index 00000000..fadbce45 --- /dev/null +++ b/docs/api.yaml @@ -0,0 +1,45 @@ +openapi: "3.0" +info: + version: "1.0.0" + title: "Plume REST API" + +servers: + - url: http://localhost:7878/api/v1 + description: Your local instance + - url: https://baptiste.gelez.xyz/api/v1 + description: Demo instance + +paths: + /posts/{id}: + get: + description: + Retrieves a post by its ID. + responses: + '200': + The post was found + '403': + The post exists, but you don't have the rights to fetch it (it is probably a private draft) + '404': + The post was not found + /posts/: + get: + description: + List posts. + +definitions: + Post: + type: "object" + properties: + title: + type: "string" + example: "Hello, world!" + id: + type: "integer" + format: "int64" + example: 42 + subtitle: + type: "string" + example: "My first post." + content: + type: "string" + format: "

This is my first post. Thanks for reading.

" diff --git a/plume-api/Cargo.toml b/plume-api/Cargo.toml new file mode 100644 index 00000000..4560e7b9 --- /dev/null +++ b/plume-api/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "plume-api" +version = "0.1.0" +authors = ["Bat' "] + +[dependencies] +canapi = "0.1" +serde = "1.0" +serde_derive = "1.0" diff --git a/plume-api/src/lib.rs b/plume-api/src/lib.rs new file mode 100644 index 00000000..a7d66d39 --- /dev/null +++ b/plume-api/src/lib.rs @@ -0,0 +1,18 @@ +extern crate canapi; +extern crate serde; +#[macro_use] +extern crate serde_derive; + +macro_rules! api { + ($url:expr => $ep:ty) => { + impl Endpoint for $ep { + type Id = i32; + + fn endpoint() -> &'static str { + $url + } + } + }; +} + +pub mod posts; diff --git a/plume-api/src/posts.rs b/plume-api/src/posts.rs new file mode 100644 index 00000000..47aa1d03 --- /dev/null +++ b/plume-api/src/posts.rs @@ -0,0 +1,11 @@ +use canapi::Endpoint; + +#[derive(Default, Serialize, Deserialize)] +pub struct PostEndpoint { + pub id: Option, + pub title: Option, + pub subtitle: Option, + pub content: Option +} + +api!("/api/v1/posts" => PostEndpoint); diff --git a/plume-models/Cargo.toml b/plume-models/Cargo.toml index a25221c6..8f634cbb 100644 --- a/plume-models/Cargo.toml +++ b/plume-models/Cargo.toml @@ -7,6 +7,7 @@ authors = ["Baptiste Gelez "] activitypub = "0.1.1" ammonia = "1.2.0" bcrypt = "0.2" +canapi = "0.1" heck = "0.3.0" lazy_static = "*" openssl = "0.10.11" @@ -25,6 +26,9 @@ version = "0.4" features = ["postgres", "r2d2", "chrono"] version = "1.3.2" +[dependencies.plume-api] +path = "../plume-api" + [dependencies.plume-common] path = "../plume-common" diff --git a/plume-models/src/comments.rs b/plume-models/src/comments.rs index 040193e0..33c20555 100644 --- a/plume-models/src/comments.rs +++ b/plume-models/src/comments.rs @@ -3,6 +3,7 @@ use activitypub::{ link, object::{Note} }; +use canapi::Provider; use chrono; use diesel::{self, PgConnection, RunQueryDsl, QueryDsl, ExpressionMethods, dsl::any}; use serde_json; diff --git a/plume-models/src/lib.rs b/plume-models/src/lib.rs index c44dfa58..71c4f4e5 100644 --- a/plume-models/src/lib.rs +++ b/plume-models/src/lib.rs @@ -3,6 +3,7 @@ extern crate activitypub; extern crate ammonia; extern crate bcrypt; +extern crate canapi; extern crate chrono; #[macro_use] extern crate diesel; @@ -10,6 +11,7 @@ extern crate heck; #[macro_use] extern crate lazy_static; extern crate openssl; +extern crate plume_api; extern crate plume_common; extern crate reqwest; extern crate rocket; diff --git a/plume-models/src/likes.rs b/plume-models/src/likes.rs index 9bf1bec3..e072a7b5 100644 --- a/plume-models/src/likes.rs +++ b/plume-models/src/likes.rs @@ -1,4 +1,5 @@ use activitypub::activity; +use canapi::Provider; use chrono; use diesel::{self, PgConnection, QueryDsl, RunQueryDsl, ExpressionMethods}; diff --git a/plume-models/src/posts.rs b/plume-models/src/posts.rs index 3d1f6219..58e93d84 100644 --- a/plume-models/src/posts.rs +++ b/plume-models/src/posts.rs @@ -3,11 +3,13 @@ use activitypub::{ link, object::{Article, Tombstone} }; +use canapi::{Error, Provider}; use chrono::{NaiveDateTime, TimeZone, Utc}; -use diesel::{self, PgConnection, RunQueryDsl, QueryDsl, ExpressionMethods, BelongingToDsl, dsl::any}; +use diesel::{self, PgConnection, RunQueryDsl, QueryDsl, ExpressionMethods, BelongingToDsl, dsl::any, Expression, BoolExpressionMethods}; use heck::KebabCase; use serde_json; +use plume_api::posts::PostEndpoint; use plume_common::activity_pub::{ Hashtag, Source, PUBLIC_VISIBILTY, Id, IntoId, @@ -55,6 +57,56 @@ pub struct NewPost { pub source: String, } +impl Provider for Post { + type Data = PostEndpoint; + + fn get(conn: &PgConnection, id: i32) -> Result { + Post::get(conn, id).map(|p| Ok(PostEndpoint { + id: Some(p.id), + title: Some(p.title.clone()), + subtitle: Some(p.subtitle.clone()), + content: Some(p.content.get().clone()) + })).unwrap_or(Err(Error::NotFound("Get Post".to_string()))) + } + + fn list(conn: &PgConnection, filter: PostEndpoint) -> Vec { + let mut filters = Vec::new(); + if let Some(title) = filter.title { + filters.push(posts::title.eq(title)); + } + + let filters = filters.into_iter(); + let res = if let Some(first_filter) = filters.next() { + posts::table.filter(filters.fold(first_filter, |q, f| q.and(f))) + .get_results::(conn) + } else { + posts::table.get_results::(conn) + }; + + res.map(|ps| ps.into_iter() + .map(|p| PostEndpoint { + id: Some(p.id), + title: Some(p.title.clone()), + subtitle: Some(p.subtitle.clone()), + content: Some(p.content.get().clone()) + }) + .collect() + ).unwrap_or(vec![]) + } + + fn create(conn: &PgConnection, query: PostEndpoint) -> Result { + + } + + fn update(conn: &PgConnection, id: i32, new_data: PostEndpoint) -> Result { + + } + + fn delete(conn: &PgConnection, id: i32) { + Post::get(conn, id).map(|p| p.delete(conn)); + } +} + impl Post { insert!(posts, NewPost); get!(posts); diff --git a/po/gl.po b/po/gl.po index 213ecff8..5977ec7a 100644 --- a/po/gl.po +++ b/po/gl.po @@ -325,7 +325,8 @@ msgstr[1] "{{ count }} autoras en este blog: " msgid "Login or use your Fediverse account to interact with this article" msgstr "" -"Conéctese ou utilice a súa conta no fediverso para interactuar con este artigo" +"Conéctese ou utilice a súa conta no fediverso para interactuar con este " +"artigo" msgid "Optional" msgstr "Opcional" @@ -486,7 +487,6 @@ msgstr "Descrición" msgid "Content warning" msgstr "Aviso sobre o contido" - msgid "File" msgstr "Ficheiro" @@ -496,7 +496,8 @@ msgstr "Enviar" msgid "" "Sorry, but registrations are closed on this instance. Try to find another one" msgstr "" -"Lamentámolo, pero o rexistro está pechado en esta instancia. Intente atopar outra" +"Lamentámolo, pero o rexistro está pechado en esta instancia. Intente atopar " +"outra" msgid "Subtitle" msgstr "Subtítulo" @@ -553,9 +554,10 @@ msgid "" "Something is wrong with your CSRF token. Make sure cookies are enabled in " "you browser, and try reloading this page. If you continue to see this error " "message, please report it." -msgstr "Hai un problema co seu testemuño CSRF. Asegúrese de ter as cookies activadas " -"no navegador, e recargue a páxina. Si persiste o aviso de este fallo, " -" informe por favor." +msgstr "" +"Hai un problema co seu testemuño CSRF. Asegúrese de ter as cookies activadas " +"no navegador, e recargue a páxina. Si persiste o aviso de este fallo, " +"informe por favor." msgid "Administration of {{ instance.name }}" msgstr "Administración de {{ instance_name }}" @@ -600,7 +602,12 @@ msgid "Delete your account" msgstr "Eliminar a súa conta" msgid "Sorry, but as an admin, you can't leave your instance." -msgstr "Lamentámolo, pero como administradora, non pode deixar a súa instancia." +msgstr "" +"Lamentámolo, pero como administradora, non pode deixar a súa instancia." msgid "Users" msgstr "Usuarias" + +#, fuzzy +msgid "This post isn't published yet." +msgstr "Esto é un borrador, non publicar por agora." diff --git a/src/api/mod.rs b/src/api/mod.rs new file mode 100644 index 00000000..83d81e78 --- /dev/null +++ b/src/api/mod.rs @@ -0,0 +1 @@ +pub mod posts; diff --git a/src/api/posts.rs b/src/api/posts.rs new file mode 100644 index 00000000..061e81d6 --- /dev/null +++ b/src/api/posts.rs @@ -0,0 +1,11 @@ +use rocket_contrib::Json; +use serde_json; + +use plume_models::db_conn::DbConn; +use plume_models::posts::Post; + +#[get("/posts/")] +fn get(id: i32, conn: DbConn) -> Json { + let post = Post::get(&*conn, id).unwrap(); + Json(post.to_json(&*conn)) +} diff --git a/src/main.rs b/src/main.rs index e121fdc0..2a499f2a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,6 +36,7 @@ use rocket_contrib::Template; use rocket_csrf::CsrfFairingBuilder; use workerpool::{Pool, thunk::ThunkWorker}; +mod api; mod inbox; mod setup; mod routes; @@ -142,6 +143,9 @@ fn main() { routes::errors::csrf_violation ]) + .mount("/api/v1", routes![ + api::posts::get + ]) .catch(catchers![ routes::errors::not_found, routes::errors::server_error