feat: Implement health checks, rate limiting, and security headers for services, update Node.js versions, and add Prettier configuration for frontend.
This commit is contained in:
@@ -17,6 +17,7 @@ tracing.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
dotenvy.workspace = true
|
||||
tower-http.workspace = true
|
||||
tower_governor.workspace = true
|
||||
reqwest.workspace = true
|
||||
bcrypt.workspace = true
|
||||
jsonwebtoken.workspace = true
|
||||
@@ -25,6 +26,8 @@ sha2.workspace = true
|
||||
hex.workspace = true
|
||||
openidconnect.workspace = true
|
||||
anyhow.workspace = true
|
||||
thiserror.workspace = true
|
||||
http.workspace = true
|
||||
zip = "0.6"
|
||||
mime_guess = "2.0"
|
||||
base64 = "0.22.1"
|
||||
|
||||
@@ -15,12 +15,17 @@ use axum::{
|
||||
middleware,
|
||||
routing::{delete, get, post, put},
|
||||
};
|
||||
use common::health::{self, HealthState};
|
||||
use dotenvy::dotenv;
|
||||
use sqlx::postgres::PgPoolOptions;
|
||||
use std::env;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tower_governor::governor::GovernorConfigBuilder;
|
||||
use tower_governor::GovernorLayer;
|
||||
use tower_http::cors::{Any, CorsLayer};
|
||||
use tower_http::set_header::SetResponseHeaderLayer;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
@@ -29,11 +34,16 @@ async fn main() {
|
||||
|
||||
let db_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
||||
let pool = PgPoolOptions::new()
|
||||
.max_connections(5)
|
||||
.max_connections(10)
|
||||
.min_connections(2)
|
||||
.acquire_timeout(Duration::from_secs(30))
|
||||
.connect(&db_url)
|
||||
.await
|
||||
.expect("Failed to connect to database");
|
||||
|
||||
// Initialize health state
|
||||
let health_state = HealthState::default();
|
||||
|
||||
sqlx::migrate!("./migrations")
|
||||
.run(&pool)
|
||||
.await
|
||||
@@ -88,6 +98,15 @@ async fn main() {
|
||||
.allow_methods(Any)
|
||||
.allow_headers(Any);
|
||||
|
||||
// Rate limiting configuration
|
||||
let governor_conf = Arc::new(
|
||||
GovernorConfigBuilder::default()
|
||||
.per_second(10)
|
||||
.burst_size(50)
|
||||
.finish()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
// Rutas protegidas que requieren autenticación y contexto de organización
|
||||
let protected_routes = Router::new()
|
||||
.route(
|
||||
@@ -299,13 +318,39 @@ async fn main() {
|
||||
"/branding",
|
||||
get(handlers_branding::get_organization_branding),
|
||||
)
|
||||
// Health check routes
|
||||
.merge(health::health_routes(pool.clone()).with_state(health_state))
|
||||
.nest_service("/assets", tower_http::services::ServeDir::new("uploads"))
|
||||
.merge(protected_routes)
|
||||
// Security headers
|
||||
.layer(SetResponseHeaderLayer::overriding(
|
||||
http::header::STRICT_TRANSPORT_SECURITY,
|
||||
http::HeaderValue::from_static("max-age=31536000; includeSubDomains"),
|
||||
))
|
||||
.layer(SetResponseHeaderLayer::overriding(
|
||||
http::header::X_CONTENT_TYPE_OPTIONS,
|
||||
http::HeaderValue::from_static("nosniff"),
|
||||
))
|
||||
.layer(SetResponseHeaderLayer::overriding(
|
||||
http::header::X_FRAME_OPTIONS,
|
||||
http::HeaderValue::from_static("SAMEORIGIN"),
|
||||
))
|
||||
.layer(SetResponseHeaderLayer::overriding(
|
||||
http::header::X_XSS_PROTECTION,
|
||||
http::HeaderValue::from_static("1; mode=block"),
|
||||
))
|
||||
.layer(SetResponseHeaderLayer::overriding(
|
||||
http::header::REFERRER_POLICY,
|
||||
http::HeaderValue::from_static("strict-origin-when-cross-origin"),
|
||||
))
|
||||
.layer(cors)
|
||||
.layer(GovernorLayer {
|
||||
config: governor_conf,
|
||||
})
|
||||
.with_state(pool);
|
||||
|
||||
let addr = SocketAddr::from(([0, 0, 0, 0], 3001));
|
||||
tracing::info!("CMS Service listening on {}", addr);
|
||||
tracing::info!("CMS Service listening on {} with rate limiting and security headers", addr);
|
||||
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
|
||||
axum::serve(listener, public_routes).await.unwrap();
|
||||
}
|
||||
|
||||
@@ -17,9 +17,12 @@ tracing.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
dotenvy.workspace = true
|
||||
tower-http.workspace = true
|
||||
tower_governor.workspace = true
|
||||
bcrypt.workspace = true
|
||||
jsonwebtoken.workspace = true
|
||||
reqwest = { version = "0.12", features = ["json"] }
|
||||
reqwest.workspace = true
|
||||
urlencoding = "2.1"
|
||||
base64 = "0.22"
|
||||
utoipa.workspace = true
|
||||
thiserror.workspace = true
|
||||
http.workspace = true
|
||||
|
||||
@@ -19,11 +19,17 @@ use axum::{
|
||||
routing::{delete, get, post, put},
|
||||
response::Html,
|
||||
};
|
||||
use common::health::{self, HealthState};
|
||||
use dotenvy::dotenv;
|
||||
use sqlx::postgres::PgPoolOptions;
|
||||
use std::env;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tower_governor::governor::GovernorConfigBuilder;
|
||||
use tower_governor::GovernorLayer;
|
||||
use tower_http::cors::{Any, CorsLayer};
|
||||
use tower_http::set_header::SetResponseHeaderLayer;
|
||||
use utoipa::OpenApi;
|
||||
|
||||
#[tokio::main]
|
||||
@@ -33,11 +39,16 @@ async fn main() {
|
||||
|
||||
let db_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
||||
let pool = PgPoolOptions::new()
|
||||
.max_connections(5)
|
||||
.max_connections(10)
|
||||
.min_connections(2)
|
||||
.acquire_timeout(Duration::from_secs(30))
|
||||
.connect(&db_url)
|
||||
.await
|
||||
.expect("Failed to connect to database");
|
||||
|
||||
// Initialize health state
|
||||
let health_state = HealthState::default();
|
||||
|
||||
let mysql_pool = external_db::init_mysql_pool().await;
|
||||
|
||||
// Run migrations automatically
|
||||
@@ -60,6 +71,15 @@ async fn main() {
|
||||
.allow_methods(Any)
|
||||
.allow_headers(Any);
|
||||
|
||||
// Rate limiting configuration
|
||||
let governor_conf = Arc::new(
|
||||
GovernorConfigBuilder::default()
|
||||
.per_second(10)
|
||||
.burst_size(50)
|
||||
.finish()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let protected_routes = Router::new()
|
||||
.route("/auth/me", get(handlers::get_me))
|
||||
.route("/enroll", post(handlers::enroll_user))
|
||||
@@ -250,6 +270,8 @@ async fn main() {
|
||||
</html>
|
||||
"#)
|
||||
}))
|
||||
// Health check routes
|
||||
.merge(health::health_routes(pool.clone()).with_state(health_state))
|
||||
.route("/catalog", get(handlers::get_course_catalog))
|
||||
.route("/ingest", post(handlers::ingest_course))
|
||||
.route("/auth/register", post(handlers::register))
|
||||
@@ -263,12 +285,36 @@ async fn main() {
|
||||
.route("/lti/jwks", get(jwks::lti_jwks_handler))
|
||||
.route("/lti/deep-linking/response", post(lti::lti_deep_linking_response))
|
||||
.merge(protected_routes)
|
||||
// Security headers
|
||||
.layer(SetResponseHeaderLayer::overriding(
|
||||
http::header::STRICT_TRANSPORT_SECURITY,
|
||||
http::HeaderValue::from_static("max-age=31536000; includeSubDomains"),
|
||||
))
|
||||
.layer(SetResponseHeaderLayer::overriding(
|
||||
http::header::X_CONTENT_TYPE_OPTIONS,
|
||||
http::HeaderValue::from_static("nosniff"),
|
||||
))
|
||||
.layer(SetResponseHeaderLayer::overriding(
|
||||
http::header::X_FRAME_OPTIONS,
|
||||
http::HeaderValue::from_static("SAMEORIGIN"),
|
||||
))
|
||||
.layer(SetResponseHeaderLayer::overriding(
|
||||
http::header::X_XSS_PROTECTION,
|
||||
http::HeaderValue::from_static("1; mode=block"),
|
||||
))
|
||||
.layer(SetResponseHeaderLayer::overriding(
|
||||
http::header::REFERRER_POLICY,
|
||||
http::HeaderValue::from_static("strict-origin-when-cross-origin"),
|
||||
))
|
||||
.layer(cors)
|
||||
.layer(GovernorLayer {
|
||||
config: governor_conf,
|
||||
})
|
||||
.with_state(pool)
|
||||
.layer(axum::Extension(mysql_pool));
|
||||
|
||||
let addr = SocketAddr::from(([0, 0, 0, 0], 3002));
|
||||
tracing::info!("LMS Service listening on {}", addr);
|
||||
tracing::info!("LMS Service listening on {} with rate limiting and security headers", addr);
|
||||
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
|
||||
axum::serve(listener, public_routes).await.unwrap();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user