Added create section endpoint
parent
8534090f0f
commit
3e7612a9a8
21
Rb.yaml
21
Rb.yaml
|
@ -21,3 +21,24 @@ debug:
|
||||||
databases:
|
databases:
|
||||||
postgres_rb:
|
postgres_rb:
|
||||||
url: "postgres://rb:rb@localhost:5432/rb"
|
url: "postgres://rb:rb@localhost:5432/rb"
|
||||||
|
|
||||||
|
# This config is just used for testing, you should change it when deploying
|
||||||
|
release:
|
||||||
|
keep_alive: 5
|
||||||
|
read_timeout: 5
|
||||||
|
write_timeout: 5
|
||||||
|
log_level: "normal"
|
||||||
|
limits:
|
||||||
|
forms: 32768
|
||||||
|
|
||||||
|
admin_user: "admin"
|
||||||
|
admin_pass: "password"
|
||||||
|
jwt:
|
||||||
|
key: "secret"
|
||||||
|
refresh_token_size: 64
|
||||||
|
# Just 5 seconds for debugging
|
||||||
|
refresh_token_expire: 60
|
||||||
|
|
||||||
|
databases:
|
||||||
|
postgres_rb:
|
||||||
|
url: "postgres://rb:rb@localhost:5432/rb"
|
||||||
|
|
|
@ -36,7 +36,7 @@ license_template_path = ""
|
||||||
make_backup = false
|
make_backup = false
|
||||||
match_arm_blocks = true
|
match_arm_blocks = true
|
||||||
match_arm_leading_pipes = "Never"
|
match_arm_leading_pipes = "Never"
|
||||||
match_block_trailing_comma = false
|
match_block_trailing_comma = true
|
||||||
max_width = 100
|
max_width = 100
|
||||||
merge_derives = true
|
merge_derives = true
|
||||||
newline_style = "Auto"
|
newline_style = "Auto"
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
|
pub mod posts;
|
||||||
|
pub mod sections;
|
||||||
pub mod tokens;
|
pub mod tokens;
|
||||||
pub mod users;
|
pub mod users;
|
||||||
pub mod sections;
|
|
||||||
pub mod posts;
|
|
||||||
|
|
||||||
|
pub use sections::{NewSection, Section};
|
||||||
pub use tokens::{NewRefreshToken, RefreshToken};
|
pub use tokens::{NewRefreshToken, RefreshToken};
|
||||||
pub use users::{NewUser, User};
|
pub use users::{NewUser, User};
|
||||||
pub use sections::{Section, NewSection};
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
use chrono::NaiveDate;
|
||||||
use diesel::{insert_into, prelude::*, Insertable, PgConnection, Queryable};
|
use diesel::{insert_into, prelude::*, Insertable, PgConnection, Queryable};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use chrono::NaiveDate;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{RbError, RbResult},
|
errors::{RbError, RbResult},
|
||||||
|
@ -28,7 +28,9 @@ pub struct NewPost
|
||||||
|
|
||||||
pub fn all(conn: &PgConnection) -> RbResult<Vec<Post>>
|
pub fn all(conn: &PgConnection) -> RbResult<Vec<Post>>
|
||||||
{
|
{
|
||||||
posts.load::<Post>(conn).map_err(|_| RbError::DbError("Couldn't get all posts."))
|
posts
|
||||||
|
.load::<Post>(conn)
|
||||||
|
.map_err(|_| RbError::DbError("Couldn't get all posts."))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(conn: &PgConnection, new_post: &NewPost) -> RbResult<()>
|
pub fn create(conn: &PgConnection, new_post: &NewPost) -> RbResult<()>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use diesel::{insert_into, prelude::*, Insertable, PgConnection, Queryable};
|
use diesel::{insert_into, prelude::*, Insertable, PgConnection, Queryable};
|
||||||
|
use serde::Deserialize;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -16,19 +17,22 @@ pub struct Section
|
||||||
pub has_titles: bool,
|
pub has_titles: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Insertable)]
|
#[derive(Deserialize, Insertable)]
|
||||||
#[table_name = "sections"]
|
#[table_name = "sections"]
|
||||||
|
// #[serde(rename_all = "camelCase")]
|
||||||
pub struct NewSection
|
pub struct NewSection
|
||||||
{
|
{
|
||||||
title: String,
|
title: String,
|
||||||
description: Option<String>,
|
description: Option<String>,
|
||||||
is_default: bool,
|
is_default: Option<bool>,
|
||||||
has_titles: bool,
|
has_titles: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn all(conn: &PgConnection) -> RbResult<Vec<Section>>
|
pub fn all(conn: &PgConnection) -> RbResult<Vec<Section>>
|
||||||
{
|
{
|
||||||
sections.load::<Section>(conn).map_err(|_| RbError::DbError("Couldn't get all sections"))
|
sections
|
||||||
|
.load::<Section>(conn)
|
||||||
|
.map_err(|_| RbError::DbError("Couldn't get all sections"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(conn: &PgConnection, new_section: &NewSection) -> RbResult<()>
|
pub fn create(conn: &PgConnection, new_section: &NewSection) -> RbResult<()>
|
||||||
|
|
|
@ -61,7 +61,7 @@ impl RbError
|
||||||
RbError::AuthInvalidRefreshToken => "This refresh token is not valid.",
|
RbError::AuthInvalidRefreshToken => "This refresh token is not valid.",
|
||||||
RbError::AuthDuplicateRefreshToken => {
|
RbError::AuthDuplicateRefreshToken => {
|
||||||
"This refresh token has already been used. The user has been blocked."
|
"This refresh token has already been used. The user has been blocked."
|
||||||
}
|
},
|
||||||
RbError::AuthMissingHeader => "Missing Authorization header.",
|
RbError::AuthMissingHeader => "Missing Authorization header.",
|
||||||
|
|
||||||
RbError::UMDuplicateUser => "This user already exists.",
|
RbError::UMDuplicateUser => "This user already exists.",
|
||||||
|
|
|
@ -10,7 +10,7 @@ use sha2::Sha256;
|
||||||
|
|
||||||
use crate::{auth::jwt::Claims, errors::RbError, RbConfig};
|
use crate::{auth::jwt::Claims, errors::RbError, RbConfig};
|
||||||
|
|
||||||
/// Extracts a "Authorization: Bearer" string from the headers.
|
/// Extracts an "Authorization: Bearer" string from the headers.
|
||||||
pub struct Bearer<'a>(&'a str);
|
pub struct Bearer<'a>(&'a str);
|
||||||
|
|
||||||
#[rocket::async_trait]
|
#[rocket::async_trait]
|
||||||
|
@ -22,7 +22,7 @@ impl<'r> FromRequest<'r> for Bearer<'r>
|
||||||
{
|
{
|
||||||
// If the header isn't present, just forward to the next route
|
// If the header isn't present, just forward to the next route
|
||||||
let header = match req.headers().get_one("Authorization") {
|
let header = match req.headers().get_one("Authorization") {
|
||||||
None => return Outcome::Failure((Status::BadRequest, Self::Error::AuthMissingHeader)),
|
None => return Outcome::Forward(()),
|
||||||
Some(val) => val,
|
Some(val) => val,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -31,12 +31,10 @@ impl<'r> FromRequest<'r> for Bearer<'r>
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the jwt token from the header
|
// Extract the jwt token from the header
|
||||||
let auth_string = match header.get(7..) {
|
match header.get(7..) {
|
||||||
Some(s) => s,
|
Some(s) => Outcome::Success(Self(s)),
|
||||||
None => return Outcome::Failure((Status::Unauthorized, Self::Error::AuthUnauthorized)),
|
None => Outcome::Failure((Status::Unauthorized, Self::Error::AuthUnauthorized)),
|
||||||
};
|
}
|
||||||
|
|
||||||
Outcome::Success(Self(auth_string))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,14 +61,14 @@ impl<'r> FromRequest<'r> for Jwt
|
||||||
Status::InternalServerError,
|
Status::InternalServerError,
|
||||||
Self::Error::Custom("Failed to do Hmac thing."),
|
Self::Error::Custom("Failed to do Hmac thing."),
|
||||||
))
|
))
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
// Verify token using key
|
// Verify token using key
|
||||||
let claims: Claims = match bearer.verify_with_key(&key) {
|
let claims: Claims = match bearer.verify_with_key(&key) {
|
||||||
Ok(claims) => claims,
|
Ok(claims) => claims,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return Outcome::Failure((Status::Unauthorized, Self::Error::AuthUnauthorized))
|
return Outcome::Failure((Status::Unauthorized, Self::Error::AuthUnauthorized))
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Outcome::Success(Self(claims))
|
Outcome::Success(Self(claims))
|
||||||
|
|
|
@ -27,6 +27,7 @@ pub mod db;
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
pub mod guards;
|
pub mod guards;
|
||||||
pub(crate) mod schema;
|
pub(crate) mod schema;
|
||||||
|
pub mod sections;
|
||||||
|
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
||||||
|
@ -111,4 +112,5 @@ fn rocket() -> _
|
||||||
"/api/admin",
|
"/api/admin",
|
||||||
routes![admin::get_users, admin::create_user, admin::get_user_info],
|
routes![admin::get_users, admin::create_user, admin::get_user_info],
|
||||||
)
|
)
|
||||||
|
.mount("/api/sections", routes![sections::create_section])
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,9 +40,4 @@ table! {
|
||||||
joinable!(posts -> sections (section_id));
|
joinable!(posts -> sections (section_id));
|
||||||
joinable!(refresh_tokens -> users (user_id));
|
joinable!(refresh_tokens -> users (user_id));
|
||||||
|
|
||||||
allow_tables_to_appear_in_same_query!(
|
allow_tables_to_appear_in_same_query!(posts, refresh_tokens, sections, users,);
|
||||||
posts,
|
|
||||||
refresh_tokens,
|
|
||||||
sections,
|
|
||||||
users,
|
|
||||||
);
|
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
use rocket::serde::json::Json;
|
||||||
|
|
||||||
|
use crate::{db, errors::RbResult, guards::Admin, RbDbConn};
|
||||||
|
|
||||||
|
/// Create a new section.
|
||||||
|
#[post("/", data = "<new_section>")]
|
||||||
|
pub async fn create_section(
|
||||||
|
_admin: Admin,
|
||||||
|
conn: RbDbConn,
|
||||||
|
new_section: Json<db::NewSection>,
|
||||||
|
) -> RbResult<()>
|
||||||
|
{
|
||||||
|
Ok(conn
|
||||||
|
.run(move |c| db::sections::create(c, &new_section.into_inner()))
|
||||||
|
.await?)
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ import requests
|
||||||
|
|
||||||
|
|
||||||
class RbClient:
|
class RbClient:
|
||||||
def __init__(self, username, password, base_url = "http://localhost:8000/api"):
|
def __init__(self, username = "admin", password = "password", base_url = "http://localhost:8000/api"):
|
||||||
self.username = username
|
self.username = username
|
||||||
self.password = password
|
self.password = password
|
||||||
self.base_url = base_url
|
self.base_url = base_url
|
||||||
|
@ -17,6 +17,7 @@ class RbClient:
|
||||||
})
|
})
|
||||||
|
|
||||||
if r.status_code != 200:
|
if r.status_code != 200:
|
||||||
|
print(r.text)
|
||||||
raise Exception("Couldn't login")
|
raise Exception("Couldn't login")
|
||||||
|
|
||||||
res = r.json()
|
res = r.json()
|
||||||
|
@ -56,9 +57,15 @@ class RbClient:
|
||||||
def get(self, url, *args, **kwargs):
|
def get(self, url, *args, **kwargs):
|
||||||
return self._request("GET", f"{self.base_url}{url}", *args, **kwargs)
|
return self._request("GET", f"{self.base_url}{url}", *args, **kwargs)
|
||||||
|
|
||||||
|
def post(self, url, *args, **kwargs):
|
||||||
|
return self._request("POST", f"{self.base_url}{url}", *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
client = RbClient("admin", "password")
|
client = RbClient()
|
||||||
|
|
||||||
print(client.get("/admin/users").json())
|
# print(client.get("/admin/users").json())
|
||||||
|
client.post("/sections", json={
|
||||||
|
"title": "this is a title"
|
||||||
|
})
|
||||||
|
|
Reference in New Issue