Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create initial project structure (Backend) #9

Merged
merged 20 commits into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[alias]
build-backend="build -p backend --features swagger --no-default-features"
build-frontend="build -p frontend --no-default-features"
run-backend="run -p backend --features swagger --no-default-features"

30 changes: 0 additions & 30 deletions .devcontainer/Dockerfile

This file was deleted.

11 changes: 0 additions & 11 deletions .devcontainer/devcontainer.json

This file was deleted.

12 changes: 11 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,14 @@ Cargo.lock
**/*.rs.bk

# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
*.pdb

/frontend/dist
/.cargo/.build
/.cargo/tmp
.env*
!.env.example
build.log
.DS_Store
/.idea/

9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Changelog

Bob Management GUI changelog

## [Unreleased]

#### Added

- Initial project structure, backend only (#9)
34 changes: 31 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,32 @@
[workspace.package]
version = "0.0.0"
authors = ["Romanov Simeon ArchArcheoss@proton.me"]
repository = "https://github.com/qoollo/bob-management"
readme = "./README.md"
license-file = "./LICENSE"
edition = "2021"

[workspace]
members = [
"bob-management-server",
]
members = [ "cli", "backend", "frontend" ]
default-members = [ "backend", "frontend" ]
resolver = "2"

[profile.release]
# Optimize for size
# opt-level = "s"
# Optimize for speed
opt-level = 3

# Slightly increase perfomance and reduce binary size
panic = "abort"

[profile.release-lto]
inherits = "release"
# Link Time optimization, causes a bit longer compilation
lto = true
# Maximize size reduction optimization, causes longer compilation
codegen-units = 1

[profile.min-size]
inherits = "release"
opt-level = "s"
2 changes: 0 additions & 2 deletions Rocket.toml

This file was deleted.

47 changes: 47 additions & 0 deletions backend/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
[package]
name = "backend"
description = "Bob Management GUI"
publish = false
keywords = [ "BOB", "Management", "GUI" ]
version.workspace = true
authors.workspace = true
license-file.workspace = true
edition.workspace = true
readme.workspace = true
repository.workspace = true

[dependencies]
# Backend (lib.rs)
## Axum related
axum = "0.6"
axum-macros = "0.3"
axum-login = "0.6"
axum-sessions = "0.5"
tower = "0.4"
tower-http = { version = "0.4", features = ["cors"] }

## Logging
tracing = "0.1"
tracing-subscriber = "0.3"

## Error Handling
error-stack = "0.4"
thiserror = "1.0"

## General
tokio = { version = "1.32", features = ["rt", "macros", "rt-multi-thread"] }
hyper = "0.14"

## OpenAPI + Swagger
utoipa = { version = "3.4", features = ["axum_extras", "chrono", "openapi_extensions"], optional = true }
utoipa-swagger-ui = { version = "3.1", features = ["axum"], optional = true }
utoipa-redoc = { version = "0.1", features = ["axum"], optional = true }
utoipa-rapidoc = { version = "0.1", features = ["axum"], optional = true }

## CLI
cli = { path = "../cli" }

[features]
default = [ "swagger" ]
swagger = [ "utoipa", "utoipa-swagger-ui" , "utoipa-redoc", "utoipa-rapidoc" ]

16 changes: 16 additions & 0 deletions backend/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use cli::Config;
use tower_http::cors::CorsLayer;

pub trait ConfigExt {
/// Return either very permissive [`CORS`](`CorsLayer`) configuration
/// or empty one based on `cors_allow_all` field
fn get_cors_configuration(&self) -> CorsLayer;
}

impl ConfigExt for Config {
fn get_cors_configuration(&self) -> CorsLayer {
self.cors_allow_all
.then_some(CorsLayer::very_permissive())
.unwrap_or_default()
}
}
1 change: 1 addition & 0 deletions backend/src/connector/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

24 changes: 24 additions & 0 deletions backend/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#![allow(clippy::module_name_repetitions)]
use axum::response::{IntoResponse, Response};
use hyper::StatusCode;
use thiserror::Error;

/// Server start up errors
#[derive(Debug, Error)]
pub enum AppError {
#[error("Server initialization failed")]
InitializationError,
#[error("Server start up failed")]
StartUpError,
}

impl IntoResponse for AppError {
fn into_response(self) -> Response {
tracing::error!("{}", self);
(
StatusCode::INTERNAL_SERVER_ERROR,
"Something went wrong".to_string(),
)
.into_response()
}
}
59 changes: 59 additions & 0 deletions backend/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#![allow(clippy::multiple_crate_versions)]

#[cfg(feature = "swagger")]
use axum::Router;
#[cfg(feature = "swagger")]
use utoipa::OpenApi;
pub mod config;
pub mod connector;
pub mod error;
pub mod models;
pub mod services;

// [TEMP]
// TODO: Remove when the actual API will be implemented
#[allow(clippy::unused_async)]
#[cfg_attr(feature = "swagger", utoipa::path(
get,
path = "/",
responses(
(status = 200, description = "Hello Bob!")
)
))]
pub async fn root() -> &'static str {
archeoss marked this conversation as resolved.
Show resolved Hide resolved
"Hello Bob!"
}

/// Generate openapi documentation for the project
#[cfg(feature = "swagger")]
pub fn openapi_doc() -> Router {
use utoipa_rapidoc::RapiDoc;
use utoipa_redoc::{Redoc, Servable};
use utoipa_swagger_ui::SwaggerUi;

/* Swagger-only routes */
#[derive(OpenApi)]
#[openapi(
paths(root),
tags(
(name = "bob", description = "BOB management API")
)
)]
struct ApiDoc;
/* Mount Swagger ui */
Router::new()
.merge(SwaggerUi::new("/swagger-ui").url("/api-docs/openapi.json", ApiDoc::openapi()))
.merge(Redoc::with_url("/redoc", ApiDoc::openapi()))
// There is no need to create `RapiDoc::with_openapi` because the OpenApi is served
// via SwaggerUi instead we only make rapidoc to point to the existing doc.
.merge(RapiDoc::new("/api-docs/openapi.json").path("/rapidoc"))
// Alternative to above
// .merge(RapiDoc::with_openapi("/api-docs/openapi2.json", ApiDoc::openapi()).path("/rapidoc"))
}

pub mod prelude {
#![allow(unused_imports)]
pub use crate::error::AppError;
pub use axum::response::Result as AxumResult;
pub use error_stack::{Context, Report, Result, ResultExt};
}
55 changes: 55 additions & 0 deletions backend/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#![allow(clippy::multiple_crate_versions)]

use axum::{routing::get, Router};
use backend::{config::ConfigExt, prelude::*, root, services::api_router};
use cli::Parser;
use error_stack::{Result, ResultExt};
use std::path::PathBuf;
use tower::ServiceBuilder;
use tower_http::cors::CorsLayer;
use tracing::Level;

#[tokio::main]
#[allow(clippy::unwrap_used, clippy::expect_used)]
async fn main() -> Result<(), AppError> {
let config = cli::Config::try_from(cli::Args::parse())
.change_context(AppError::InitializationError)
.attach_printable("Couldn't get config file.")?;

let logger = &config.logger;

init_tracer(&logger.log_file, logger.trace_level);
tracing::info!("Logger: {logger:?}");

let cors: CorsLayer = config.get_cors_configuration();
tracing::info!("CORS: {cors:?}");

let addr = config.address;
tracing::info!("Listening on {addr}");

let app = router(cors);
#[cfg(feature = "swagger")]
let app = app.merge(backend::openapi_doc());

axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.change_context(AppError::StartUpError)
.attach_printable("Failed to start axum server")?;

Ok(())
}

fn init_tracer(_log_file: &Option<PathBuf>, trace_level: Level) {
archeoss marked this conversation as resolved.
Show resolved Hide resolved
let subscriber = tracing_subscriber::fmt().with_max_level(trace_level);
subscriber.init();
}

fn router(cors: CorsLayer) -> Router {
// Add api
Router::new()
// Unsecured Routes
.route("/", get(root))
.nest("/api", api_router())
.layer(ServiceBuilder::new().layer(cors))
}
1 change: 1 addition & 0 deletions backend/src/models/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

30 changes: 30 additions & 0 deletions backend/src/services/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use axum::{
response::{IntoResponse, Response},
Router,
};
use hyper::{Body, StatusCode};
use thiserror::Error;

/// Export all secured routes
#[allow(dead_code)]
pub fn api_router() -> Router<(), Body> {
Router::new()
}

/// Errors that happend during API request proccessing
#[derive(Debug, Error)]
pub enum APIError {
#[error("The request to the specified resource failed")]
RequestFailed,
#[error("Server received invalid status code from client: `{0}`")]
InvalidStatusCode(StatusCode),
}

impl IntoResponse for APIError {
fn into_response(self) -> Response {
match self {
Self::RequestFailed => (StatusCode::NOT_FOUND, self.to_string()).into_response(),
Self::InvalidStatusCode(code) => code.into_response(),
}
}
}
21 changes: 0 additions & 21 deletions bob-management-server/Cargo.toml

This file was deleted.

Loading