Skip to content

Commit

Permalink
Update v0.4.0 to new axum 0.6 (#9)
Browse files Browse the repository at this point in the history
* initial refactor to axum 0.6

* modified how state is used with Store for Axum 0.6

* comments and cargo fmt

* updated readme
  • Loading branch information
jbertovic authored Jan 8, 2023
1 parent aa8dd6d commit 9004aa4
Show file tree
Hide file tree
Showing 9 changed files with 293 additions and 215 deletions.
406 changes: 244 additions & 162 deletions Cargo.lock

Large diffs are not rendered by default.

17 changes: 10 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
# New Version 0.4.0
- updated to `axum` 0.6
- changes to State usage; how its setup with route and called from middleware
- changes to ordering of parameters in functions; last parameter can consume request body
- eliminated `axum::extract::RequestParts` with help from update on `axum-sessions`
- updated to `axum-sessions` 0.4 to match
- incremented `tokio` version to 1.24
- old `axum` 0.5 version is kept under branch `v0.3_Axum0.5`

# New Version 0.3.0
- switched to using Vite as per current template setup from svelte [https://svelte.dev/](https://svelte.dev/)
- changed directory structure with ./front_end/public and location of index.html
- modified backend to use `./front_end/dist` for static serve - location of `npm run build`
- cleaned up css and added app.css and global css

# New Version 0.2.0
- Will create breaking changes since I modifed the folder structure to use Cargo Workspaces
- Front end and Back end are in two different project folders instead of just different src folders
- Workspaces now allow you to add Rust code independent of the back end server setup
- Also refactored some of the setup out of main.rs into a server.rs file

# Help
- If anyone has ideas of how to implement a build file that would handle the npm script build on the front end and the rust cargo run together, let me know.

Expand Down Expand Up @@ -50,7 +53,7 @@ run as `cargo run` from parent directory and not needed to run inside `./back_en
- secure page that shows session information once logged in
- api fetch example, log in not required

run as `npm run build` from inside the `./front_end` directory
run as `npm run build` from inside the `./front_end` directory to build the static serve file directory.

# Setup

Expand Down
9 changes: 4 additions & 5 deletions back_end/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "svelte-axum-project"
version = "0.3.0"
version = "0.4.0"
edition = "2021"
repository = "https://github.com/jbertovic/svelte-axum-project"
keywords = ["template", "backend", "frontend", "axum", "svelte"]
Expand All @@ -11,11 +11,10 @@ readme = "README.md"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
axum = { version="0.5.16", features = ["headers"] }
axum = { version="0.6.1", features = ["headers"] }
tower-http = { version = "0.3", features = ["full"] }
axum-sessions = "0.3"
tokio = { version = "1.21", features = ["full"] }
axum-sessions = "0.4"
tokio = { version = "1.24", features = ["full"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
uuid = { version = "1.1", features = ["v4", "serde"] }
serde = { version = "1.0", features = ["derive"] }
1 change: 1 addition & 0 deletions back_end/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ async fn main() {
let session_layer = SessionLayer::new(MemoryStore::new(), secret.as_bytes())
.with_cookie_name(SESSION_COOKIE_NAME);

// combine the front and backend into server
let app = Router::new()
.merge(services::front_public_route())
.merge(services::backend(session_layer, shared_state));
Expand Down
21 changes: 7 additions & 14 deletions back_end/src/middlewares/authenticator.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::sync::Arc;

use axum::{
extract::State,
http::{self, Request, StatusCode},
middleware::Next,
response::Response,
Expand All @@ -17,6 +18,7 @@ use crate::store::Store;
/// Returns Error's in JSON format.
#[allow(clippy::missing_errors_doc)]
pub async fn auth<B: Send + Sync>(
State(store): State<Arc<Store>>,
req: Request<B>,
next: Next<B>,
) -> Result<Response, (StatusCode, Json<JsonError>)> {
Expand All @@ -35,21 +37,12 @@ pub async fn auth<B: Send + Sync>(
tracing::debug!("Received Authorization Header: {}", auth_header);

// check bearer authorization to see if it matches
if let Some(store) = req.extensions().get::<Arc<Store>>() {
if store.api_token_check(auth_header) {
Ok(next.run(req).await)
} else {
tracing::debug!("Authorization token does NOT match");
// return Ok(Json(json!( {"error": "Unauthorized"} )).into_response());
Err((StatusCode::UNAUTHORIZED, Json(JsonError::unauthorized())))
}
if store.api_token_check(auth_header) {
Ok(next.run(req).await)
} else {
tracing::debug!("Can't retrieve Store");
// return Ok(Json(json!( {"error": "Internal Server Error"} )).into_response());
Err((
StatusCode::INTERNAL_SERVER_ERROR,
Json(JsonError::internal()),
))
tracing::debug!("Authorization token does NOT match");
// return Ok(Json(json!( {"error": "Unauthorized"} )).into_response());
Err((StatusCode::UNAUTHORIZED, Json(JsonError::unauthorized())))
}
}

Expand Down
14 changes: 5 additions & 9 deletions back_end/src/middlewares/usersecure.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
use axum::{
extract::RequestParts,
http::{Request, StatusCode},
middleware::Next,
response::Response,
};
use axum_sessions::extractors::ReadableSession;

#[allow(clippy::missing_errors_doc)]
pub async fn user_secure<B: Send>(req: Request<B>, next: Next<B>) -> Result<Response, StatusCode> {
pub async fn user_secure<B: Send>(
session: ReadableSession,
req: Request<B>,
next: Next<B>,
) -> Result<Response, StatusCode> {
tracing::info!("Middleware: checking if user exists");
let mut request_parts = RequestParts::new(req);
let session = request_parts
.extract::<ReadableSession>()
.await
.map_err(|_| StatusCode::UNAUTHORIZED)?;
let user_id = session.get_raw("user_id").ok_or(StatusCode::UNAUTHORIZED)?;
tracing::debug!("user_id Extracted: {}", user_id);

// accepts all user but you could add a check here to match user access

let req = request_parts.try_into_request().expect("body extracted");
Ok(next.run(req).await)
}
2 changes: 1 addition & 1 deletion back_end/src/routes/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use serde::Deserialize;
/// route to handle log in
#[allow(clippy::unused_async)]
#[allow(clippy::missing_panics_doc)]
pub async fn login(Json(login): Json<Login>, mut session: WritableSession) -> impl IntoResponse {
pub async fn login(mut session: WritableSession, Json(login): Json<Login>) -> impl IntoResponse {
tracing::info!("Logging in user: {}", login.username);

if check_password(&login.username, &login.password) {
Expand Down
36 changes: 20 additions & 16 deletions back_end/src/services.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
use axum::{
body::Body,
http::StatusCode,
middleware,
response::IntoResponse,
routing::{get, get_service, post},
Extension, Router,
Router,
};
use axum_sessions::{async_session::SessionStore, SessionLayer};
use std::{io, sync::Arc};
use tower_http::{services::ServeDir, trace::TraceLayer};

use crate::{middlewares, routes, store, FRONT_PUBLIC};
use crate::{
middlewares, routes,
store::{self, Store},
FRONT_PUBLIC,
};

// *********
// FRONT END
// *********
// Front end to server svelte build bundle, css and index.html from public folder
pub fn front_public_route() -> Router<Body> {
pub fn front_public_route() -> Router {
Router::new()
.fallback(get_service(ServeDir::new(FRONT_PUBLIC)).handle_error(handle_error))
.fallback_service(get_service(ServeDir::new(FRONT_PUBLIC)).handle_error(handle_error))
.layer(TraceLayer::new_for_http())
}

Expand All @@ -34,28 +37,24 @@ async fn handle_error(_err: io::Error) -> impl IntoResponse {
// BACK END
// ********
// Back end server built form various routes that are either public, require auth, or secure login
pub fn backend<Store>(
pub fn backend<Store: SessionStore>(
session_layer: SessionLayer<Store>,
shared_state: Arc<store::Store>,
) -> Router<Body>
where
Store: SessionStore,
{
) -> Router {
// could add tower::ServiceBuilder here to group layers, especially if you add more layers.
// see https://docs.rs/axum/latest/axum/middleware/index.html#ordering
Router::new()
.merge(back_public_route())
.merge(back_auth_route())
.merge(back_token_route())
.merge(back_token_route(shared_state))
.layer(session_layer)
.layer(Extension(shared_state))
}

// *********
// BACKEND NON-AUTH
// *********
//
pub fn back_public_route() -> Router<Body> {
pub fn back_public_route() -> Router {
Router::new()
.route("/auth/session", get(routes::session::data_handler)) // gets session data
.route("/auth/login", post(routes::login)) // sets username in session
Expand All @@ -67,7 +66,7 @@ pub fn back_public_route() -> Router<Body> {
// BACKEND SESSION
// *********
//
pub fn back_auth_route() -> Router<Body> {
pub fn back_auth_route() -> Router {
Router::new()
.route("/secure", get(routes::session::handler))
.route_layer(middleware::from_fn(middlewares::user_secure))
Expand All @@ -77,8 +76,13 @@ pub fn back_auth_route() -> Router<Body> {
// BACKEND API
// *********
//
pub fn back_token_route() -> Router<Body> {
// invoked with State that stores API that is checked by the `middleware::auth`
pub fn back_token_route<S>(state: Arc<Store>) -> Router<S> {
Router::new()
.route("/api", get(routes::api::handler))
.route_layer(middleware::from_fn(middlewares::auth))
.route_layer(middleware::from_fn_with_state(
state.clone(),
middlewares::auth,
))
.with_state(state)
}
2 changes: 1 addition & 1 deletion front_end/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "svelte-axum-project",
"description": "svelte front end with axum backend template",
"author": "Jas Bertovic",
"version": "0.3.0",
"version": "0.4.0",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down

0 comments on commit 9004aa4

Please sign in to comment.