Skip to content

Commit

Permalink
new file: .env
Browse files Browse the repository at this point in the history
	new file:   .gitignore
	new file:   Cargo.toml
	new file:   src/entities/cart.rs
	new file:   src/entities/category.rs
	new file:   src/entities/mod.rs
	new file:   src/entities/product.rs
	new file:   src/entities/user.rs
	new file:   src/main.rs
	new file:   src/middleware/auth.rs
	new file:   src/middleware/mod.rs
	new file:   src/routes/auth_routes.rs
	new file:   src/routes/mod.rs
	new file:   tests/auth.rs
  • Loading branch information
Tapo-o-chka committed Dec 30, 2024
1 parent ec2ab99 commit bd766e7
Show file tree
Hide file tree
Showing 14 changed files with 568 additions and 0 deletions.
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DATABASE_URL=sqlite::memory:
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/target
/Cargo.lock
/TODO
20 changes: 20 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "rust-baranki"
version = "0.1.0"
edition = "2021"

[dependencies]
axum = "0.7.9"
serde = { version = "1.0.215", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1.41.1", features = ["full"] }
sea-orm = { version = "1.1.2", features = ["sqlx-sqlite", "runtime-tokio-rustls", "macros"] }
tracing-subscriber = { version = "0.3.19", features = ["fmt", "env-filter"] }
dotenvy = "0.15"
argon2 = "0.4"
chrono = { version = "0.4", features = ["serde"] }
jsonwebtoken = "8.3"
rand = "0.8.5"

[dev-dependencies]
reqwest = { version = "0.11", features = ["json"] }
37 changes: 37 additions & 0 deletions src/entities/cart.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use sea_orm::entity::prelude::*;
use crate::entities::user::Entity as User;
use crate::entities::product::Entity as Product;

#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "cart")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
#[sea_orm(indexed)]
pub user_id: i32,
pub item_id: i32,
pub quantity: i32,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "User",
from = "crate::entities::cart::Column::UserId",
to = "crate::entities::user::Column::Id",
on_update = "Cascade",
on_delete = "Cascade"
)]
User,
#[sea_orm(
belongs_to = "Product",
from = "crate::entities::cart::Column::ItemId",
to = "crate::entities::product::Column::Id",
on_update = "Cascade",
on_delete = "Cascade"
)]
Product,
}


impl ActiveModelBehavior for ActiveModel {}
20 changes: 20 additions & 0 deletions src/entities/category.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use sea_orm::entity::prelude::*;

#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "category")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
#[sea_orm(unique)]
pub name: String,
pub image_url: String,
#[sea_orm(default = false)]
pub is_featured: bool,
#[sea_orm(default = true)]
pub is_available: bool,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}

impl ActiveModelBehavior for ActiveModel {}
4 changes: 4 additions & 0 deletions src/entities/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod user;
pub mod product;
pub mod cart;
pub mod category;
34 changes: 34 additions & 0 deletions src/entities/product.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use sea_orm::entity::prelude::*;
use crate::entities::category::Entity as Category;

#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "products")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
#[sea_orm(unique)]
pub name: String,
pub price: f32,
#[sea_orm(column_type = "Text")]
pub description: String,
pub image_url: String,
pub category_id: i32,
#[sea_orm(default = false)]
pub is_featured: bool,
#[sea_orm(default = true)]
pub is_available: bool,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "Category",
from = "crate::entities::product::Column::CategoryId",
to = "crate::entities::category::Column::Id",
on_update = "Cascade",
on_delete = "Cascade"
)]
Category,
}

impl ActiveModelBehavior for ActiveModel {}
37 changes: 37 additions & 0 deletions src/entities/user.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use sea_orm::entity::prelude::*;

use argon2::{
password_hash::PasswordVerifier,
Argon2,
PasswordHash,
};

//use crate::entity::jwt_token;

#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "users")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
#[sea_orm(unique)]
pub username: String,
pub password: String,
}

impl Model {
pub fn check_hash(&self, password: &str) -> Result<(), String> {
let parsed_hash = PasswordHash::new(&self.password).unwrap();

let argon2 = Argon2::default();
argon2.verify_password(password.as_bytes(), &parsed_hash)
.map_err(|_| "Password verification failed")?;

Ok(())
}
}


#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}

impl ActiveModelBehavior for ActiveModel {}
64 changes: 64 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use axum::{routing::get, Router};
use sea_orm::{ConnectionTrait, Schema};
use sea_orm::{Database, DatabaseConnection};
use std::sync::Arc;
use tokio::sync::Mutex;

mod entities;
mod routes;
mod middleware;

use crate::entities::{
cart::Entity as Crate,
category::Entity as Category,
user::Entity as User,
product::Entity as Product,
};
use crate::routes::auth_routes::auth_routes;

pub async fn setup_schema(db: &DatabaseConnection) {
let schema = Schema::new(db.get_database_backend());
let create_cart_table = schema.create_table_from_entity(Crate);
let create_category_table = schema.create_table_from_entity(Category);
let create_user_table = schema.create_table_from_entity(User);
let create_product_table = schema.create_table_from_entity(Product);

db.execute(db.get_database_backend().build(&create_cart_table))
.await
.expect("Failed to create cart schema");
db.execute(db.get_database_backend().build(&create_category_table))
.await
.expect("Failed to create category schema");
db.execute(db.get_database_backend().build(&create_user_table))
.await
.expect("Failed to create user schema");
db.execute(db.get_database_backend().build(&create_product_table))
.await
.expect("Failed to create product schema");
}


#[tokio::main]
async fn main() {
tracing_subscriber::fmt::init();

dotenvy::dotenv().ok();

let database_url = std::env::var("DATABASE_URL").expect("Databse url must be set");
let db: DatabaseConnection = Database::connect(&database_url).await.unwrap();

setup_schema(&db).await;

let shared_db = Arc::new(Mutex::new(db));
let user_routes = auth_routes(shared_db.clone()).await;

let app = Router::new().route("/", get(root)).nest("/", user_routes);

let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
println!("Running at {:?}", listener);
axum::serve(listener, app).await.unwrap();
}

async fn root() -> &'static str {
"Hello, World!"
}
64 changes: 64 additions & 0 deletions src/middleware/auth.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use axum::{
body::Body,
extract::Request,
http::StatusCode,
middleware::Next,
response::IntoResponse,
Json,
};
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
use serde::{Deserialize, Serialize};

const SECRET: &str = "very_secret";

pub async fn jwt_middleware(mut req: Request<Body>, next: Next) -> impl IntoResponse {
if let Some(auth_header) = req.headers().get("Authorization") {
if let Ok(auth_header) = auth_header.to_str() {
if auth_header.starts_with("Bearer ") {
let token: &str = &auth_header[7..];
match validate_token(token) {
Ok(claims) => {
req.extensions_mut().insert(claims.username.clone());
return next.run(req).await;
}
Err(_) => {
return (
StatusCode::UNAUTHORIZED,
Json(ResponseMessage {
message: "Invalid token".to_string(),
}),
)
.into_response();
}
}
}
}
}
(
StatusCode::UNAUTHORIZED,
Json(ResponseMessage {
message: "Missing or invalid Authorization header".to_string(),
}),
)
.into_response()
}

fn validate_token(token: &str) -> Result<Claims, jsonwebtoken::errors::Error> {
decode::<Claims>(
token,
&DecodingKey::from_secret(SECRET.as_ref()),
&Validation::new(Algorithm::HS256),
)
.map(|data| data.claims)
}

#[derive(Debug, Serialize, Deserialize)]
struct ResponseMessage {
message: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct Claims {
username: String,
exp: usize,
}
1 change: 1 addition & 0 deletions src/middleware/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod auth;
Loading

0 comments on commit bd766e7

Please sign in to comment.