Skip to content

Commit 5a73689

Browse files
committed
Post endpoints
1 parent 7fc09e0 commit 5a73689

File tree

5 files changed

+177
-16
lines changed

5 files changed

+177
-16
lines changed

src/api/post.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
use std::path::PathBuf;
2+
use actix_web::{Error, get, HttpResponse, patch, put, Scope, web, post, delete};
3+
use actix_web::web::Json;
4+
use futures_util::StreamExt;
5+
use log::error;
6+
use crate::definitions::{BodyPost, BodyUser, Post, User};
7+
use crate::storage::database_manager::DatabaseManager;
8+
use crate::storage::storage_manager::StorageManager;
9+
10+
pub fn blog_service() -> Scope {
11+
web::scope("/api/v1/posts")
12+
.service(posts_get)
13+
.service(post_get)
14+
.service(post_delete)
15+
.service(post_post)
16+
}
17+
18+
#[get("")]
19+
async fn posts_get(
20+
user: User,
21+
database_manager: web::Data<DatabaseManager>) -> Result<HttpResponse, Error> {
22+
23+
if !user.admin {
24+
return Ok(HttpResponse::Unauthorized().finish())
25+
}
26+
27+
let posts = match database_manager.fetch_posts().await {
28+
Ok(posts) => posts,
29+
Err(_) => {
30+
return Ok(HttpResponse::InternalServerError().finish());
31+
}
32+
};
33+
Ok(HttpResponse::Ok().json(posts))
34+
}
35+
36+
#[get("/{postId}")]
37+
async fn post_get(
38+
path: web::Path<String>,
39+
database_manager: web::Data<DatabaseManager>) -> Result<HttpResponse, Error> {
40+
41+
let post_id = path.into_inner();
42+
43+
let found_post: Option<Post> = match database_manager.fetch_post(post_id).await {
44+
Ok(found_post) => {
45+
found_post
46+
}
47+
Err(_) => {
48+
return Ok(HttpResponse::InternalServerError().finish());
49+
}
50+
};
51+
52+
match found_post {
53+
Some(found_user) => {
54+
Ok(HttpResponse::Ok().json(found_user))
55+
}
56+
None => {
57+
Ok(HttpResponse::NotFound().finish())
58+
}
59+
}
60+
}
61+
62+
#[delete("/{postId}")]
63+
async fn post_delete(
64+
user: User,
65+
path: web::Path<String>,
66+
database_manager: web::Data<DatabaseManager>) -> Result<HttpResponse, Error> {
67+
68+
let post_id = path.into_inner();
69+
70+
if !user.admin {
71+
return Ok(HttpResponse::Unauthorized().finish())
72+
}
73+
74+
match database_manager.delete_post(post_id).await {
75+
Ok(_) => {
76+
Ok(HttpResponse::Ok().finish())
77+
},
78+
Err(_) => {
79+
Ok(HttpResponse::InternalServerError().finish())
80+
}
81+
}
82+
}
83+
84+
#[post("")]
85+
async fn post_post(
86+
user: User,
87+
body: Json<BodyPost>,
88+
database_manager: web::Data<DatabaseManager>) -> Result<HttpResponse, Error> {
89+
90+
if !user.admin {
91+
return Ok(HttpResponse::Unauthorized().finish())
92+
}
93+
94+
let post = body.into_inner();
95+
96+
match database_manager.add_post(post).await {
97+
Ok(_) => {
98+
}
99+
Err(err) => {
100+
error!("Could not post post {err}");
101+
return Ok(HttpResponse::InternalServerError().finish())
102+
}
103+
}
104+
105+
Ok(HttpResponse::Ok().finish())
106+
}

src/auth.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use actix_web::web::{Data, Json};
77
use actix_web_httpauth::extractors::bearer::BearerAuth;
88
use jsonwebtoken::{encode, decode, Header as JwtHeader, Algorithm, Validation, EncodingKey, DecodingKey, errors::Result as JwtResult};
99
use serde::{Deserialize, Serialize};
10-
use crate::definitions::User;
10+
use crate::definitions::{User};
1111
use crate::storage::database_manager::DatabaseManager;
1212

1313
#[derive(Debug, Serialize, Deserialize)]
@@ -111,7 +111,7 @@ async fn auth_login(
111111
if user.validate_password(login_credentials.password.clone()) {
112112
let iat = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs() as usize;
113113
let claims = Claims {
114-
exp: iat + (24 * 60 * 60),
114+
exp: iat + (30 * 24 * 60 * 60),
115115
iat,
116116
iss: "intelligence".to_string(),
117117
sub: user.id.to_string(),

src/definitions.rs

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
11
use std::cmp::PartialEq;
22
use std::fmt;
33
use serde::{Deserialize, Deserializer, Serialize, Serializer};
4+
use surrealdb::Datetime;
45
use surrealdb::sql::Id;
56

7+
#[derive(Debug, Serialize, Deserialize, Clone)]
8+
pub struct IntelliThing {
9+
pub(crate) id: Id,
10+
}
11+
612
impl fmt::Display for IntelliThing {
713
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
814
write!(f, "{}", self.id)
915
}
1016
}
1117

12-
#[derive(Debug, Serialize, Deserialize, Clone)]
13-
pub struct IntelliThing {
14-
pub(crate) id: Id,
18+
impl PartialEq for IntelliThing {
19+
fn eq(&self, other: &Self) -> bool {
20+
other.id == self.id
21+
}
1522
}
1623

1724
#[derive(Serialize, Deserialize, Debug)]
@@ -27,10 +34,16 @@ pub struct User {
2734
pub(crate) lastname: Option<String>,
2835
}
2936

30-
impl PartialEq for IntelliThing {
31-
fn eq(&self, other: &Self) -> bool {
32-
other.id == self.id
33-
}
37+
#[derive(Serialize, Deserialize, Debug)]
38+
pub struct Post {
39+
#[serde(serialize_with = "serialize_record_id")]
40+
pub(crate) id: IntelliThing,
41+
#[serde(serialize_with = "serialize_record_id")]
42+
pub(crate) author: IntelliThing,
43+
pub(crate) likes: i32,
44+
pub(crate) views: i32,
45+
pub(crate) title: String,
46+
pub(crate) posted: Datetime,
3447
}
3548

3649
impl User {
@@ -59,6 +72,16 @@ pub struct BodyUser {
5972
pub(crate) lastname: Option<String>,
6073
}
6174

75+
// Used by the http endpoint to allow patching the post
76+
#[derive(Serialize, Deserialize, Debug)]
77+
pub struct BodyPost {
78+
#[serde(serialize_with = "serialize_option_record_id", deserialize_with = "deserialize_record_id")]
79+
pub(crate) author: Option<IntelliThing>,
80+
pub(crate) likes: Option<i32>,
81+
pub(crate) views: Option<i32>,
82+
pub(crate) title: Option<String>,
83+
pub(crate) posted: Option<Datetime>,
84+
}
6285

6386
fn serialize_record_id<S>(record_id: &IntelliThing, serializer: S) -> Result<S::Ok, S::Error>
6487
where

src/main.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey};
1111
use crate::auth::auth_service;
1212

1313
mod api { // Declare the 'api' module
14-
14+
pub mod post;
1515
pub mod users;
1616
}
1717

@@ -64,6 +64,7 @@ async fn main() -> Result<(), Error> {
6464

6565
.service(auth_service())
6666
.service(api::users::user_service())
67+
.service(api::post::blog_service())
6768
})
6869
.workers(2)
6970
.bind("0.0.0.0:6969")?

src/storage/database_manager.rs

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use surrealdb::engine::remote::ws::{Client, Ws};
44
use surrealdb::opt::auth::{Root};
55
use surrealdb::{Response, Surreal};
66
use log::info;
7-
use crate::definitions::{BodyUser, User};
7+
use crate::definitions::{BodyPost, BodyUser, IntelliThing, Post, User};
88

99
#[derive(Clone)]
1010
pub struct DatabaseManager {
@@ -58,11 +58,6 @@ impl DatabaseManager {
5858
Ok(users)
5959
}
6060

61-
pub async fn delete_user(&self, id: String) -> surrealdb::Result<Option<User>> {
62-
let deleted: Option<User> = self.database.delete(("user", id)).await?;
63-
Ok(deleted)
64-
}
65-
6661
pub async fn fetch_user(&self, name_or_email: String) -> surrealdb::Result<Option<User>> {
6762
let user: Vec<User> = self.database
6863
.query("SELECT * FROM user WHERE name = $name OR email = $name OR id = type::thing(\"user\", $name) LIMIT 1")
@@ -73,13 +68,49 @@ impl DatabaseManager {
7368
Ok(user.into_iter().nth(0))
7469
}
7570

71+
pub async fn fetch_posts(&self) -> surrealdb::Result<Vec<Post>> {
72+
let posts: Vec<Post> = self.database
73+
.query("SELECT * FROM post ORDER BY posted ASC")
74+
.await?
75+
.take(0)?;
76+
77+
Ok(posts)
78+
}
79+
80+
pub async fn fetch_post(&self, title_or_id: String) -> surrealdb::Result<Option<Post>> {
81+
let post: Vec<Post> = self.database
82+
.query("SELECT * FROM post WHERE title = $name OR id = type::thing(\"post\", $name) LIMIT 1")
83+
.bind(("name", title_or_id))
84+
.await?
85+
.take(0)?;
86+
87+
Ok(post.into_iter().nth(0))
88+
}
89+
90+
pub async fn delete_user(&self, id: String) -> surrealdb::Result<Option<User>> {
91+
let deleted: Option<User> = self.database.delete(("user", id)).await?;
92+
Ok(deleted)
93+
}
94+
95+
pub async fn delete_post(&self, id: String) -> surrealdb::Result<Option<Post>> {
96+
let deleted: Option<Post> = self.database.delete(("post", id)).await?;
97+
Ok(deleted)
98+
}
99+
76100
pub async fn add_user(&self, user: BodyUser) -> surrealdb::Result<Vec<User>> {
77101
self.database
78102
.insert("user")
79103
.content(user)
80104
.await
81105
}
82106

107+
pub async fn add_post(&self, post: BodyPost) -> surrealdb::Result<Vec<Post>> {
108+
self.database
109+
.insert("post")
110+
.content(post)
111+
.await
112+
}
113+
83114
pub async fn update_user(&self, user: &User) -> surrealdb::Result<Option<User>> {
84115
self.database
85116
.update(("user", user.id.to_string()))

0 commit comments

Comments
 (0)