diff --git a/Cargo.lock b/Cargo.lock index b2d2e3ce..c5208998 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,21 +19,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "actix-cors" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9e772b3bcafe335042b5db010ab7c09013dad6eac4915c91d8d50902769f331" -dependencies = [ - "actix-utils", - "actix-web", - "derive_more", - "futures-util", - "log", - "once_cell", - "smallvec", -] - [[package]] name = "actix-http" version = "3.8.0" @@ -73,16 +58,6 @@ dependencies = [ "zstd", ] -[[package]] -name = "actix-macros" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" -dependencies = [ - "quote", - "syn 2.0.68", -] - [[package]] name = "actix-router" version = "0.5.3" @@ -173,18 +148,15 @@ checksum = "1988c02af8d2b718c05bc4aeb6a66395b7cdf32858c2c71131e5637a8c05a9ff" dependencies = [ "actix-codec", "actix-http", - "actix-macros", "actix-router", "actix-rt", "actix-server", "actix-service", "actix-utils", - "actix-web-codegen", "ahash", "bytes", "bytestring", "cfg-if", - "cookie", "derive_more", "encoding_rs", "futures-core", @@ -195,7 +167,6 @@ dependencies = [ "mime", "once_cell", "pin-project-lite", - "regex", "regex-lite", "serde", "serde_json", @@ -206,18 +177,6 @@ dependencies = [ "url", ] -[[package]] -name = "actix-web-codegen" -version = "4.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8" -dependencies = [ - "actix-router", - "proc-macro2", - "quote", - "syn 2.0.68", -] - [[package]] name = "actix-web-validator" version = "6.0.0" @@ -455,6 +414,37 @@ dependencies = [ "tokio", ] +[[package]] +name = "axum" +version = "0.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acee9fd5073ab6b045a275b3e709c163dd36c90685219cb21804a147b58dba43" +dependencies = [ + "async-trait", + "axum-core 0.2.9", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.30", + "itoa", + "matchit 0.5.0", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-http", + "tower-layer", + "tower-service", +] + [[package]] name = "axum" version = "0.6.20" @@ -462,7 +452,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" dependencies = [ "async-trait", - "axum-core", + "axum-core 0.3.4", "bitflags 1.3.2", "bytes", "futures-util", @@ -470,7 +460,7 @@ dependencies = [ "http-body 0.4.6", "hyper 0.14.30", "itoa", - "matchit", + "matchit 0.7.3", "memchr", "mime", "percent-encoding", @@ -483,6 +473,22 @@ dependencies = [ "tower-service", ] +[[package]] +name = "axum-core" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e5939e02c56fecd5c017c37df4238c0a839fa76b7f97acdd7efb804fd181cc" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "mime", + "tower-layer", + "tower-service", +] + [[package]] name = "axum-core" version = "0.3.4" @@ -1359,6 +1365,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-range-header" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" + [[package]] name = "httparse" version = "1.9.4" @@ -1714,6 +1726,12 @@ dependencies = [ "regex-automata", ] +[[package]] +name = "matchit" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb" + [[package]] name = "matchit" version = "0.7.3" @@ -1791,21 +1809,16 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "mutually_exclusive_features" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d02c0b00610773bb7fc61d85e13d86c7858cbdf00e1a120bfc41bc055dbaa0e" - [[package]] name = "nittei" version = "0.1.0" dependencies = [ - "actix-web", "anyhow", + "axum 0.5.17", "chrono", "chrono-tz", "futures", + "hyper 0.14.30", "nittei_api", "nittei_domain", "nittei_infra", @@ -1820,6 +1833,7 @@ dependencies = [ "test-log", "tikv-jemallocator", "tokio", + "tower", "tracing", "tracing-opentelemetry", "tracing-subscriber", @@ -1829,15 +1843,15 @@ dependencies = [ name = "nittei_api" version = "0.1.0" dependencies = [ - "actix-cors", - "actix-web", "actix-web-validator", "anyhow", "async-trait", "awc", + "axum 0.5.17", "chrono", "chrono-tz", "futures", + "hyper 0.14.30", "jsonwebtoken", "nittei_api_structs", "nittei_domain", @@ -1849,8 +1863,8 @@ dependencies = [ "serial_test", "thiserror", "tokio", + "tower", "tracing", - "tracing-actix-web", "tracing-futures", "validator", ] @@ -3751,7 +3765,7 @@ checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13" dependencies = [ "async-stream", "async-trait", - "axum", + "axum 0.6.20", "base64 0.21.7", "bytes", "h2 0.3.26", @@ -3793,6 +3807,25 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower-http" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" +dependencies = [ + "bitflags 1.3.2", + "bytes", + "futures-core", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "http-range-header", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.2" @@ -3817,21 +3850,6 @@ dependencies = [ "tracing-core", ] -[[package]] -name = "tracing-actix-web" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee9e39a66d9b615644893ffc1704d2a89b5b315b7fd0228ad3182ca9a306b19" -dependencies = [ - "actix-web", - "mutually_exclusive_features", - "opentelemetry", - "pin-project", - "tracing", - "tracing-opentelemetry", - "uuid", -] - [[package]] name = "tracing-attributes" version = "0.1.27" diff --git a/bins/nittei/Cargo.toml b/bins/nittei/Cargo.toml index 2cfe1489..14ec0663 100644 --- a/bins/nittei/Cargo.toml +++ b/bins/nittei/Cargo.toml @@ -16,7 +16,9 @@ nittei_utils = { workspace = true } anyhow = "1.0" -actix-web = "4.8" +axum = "0.5" +tower = "0.4" +hyper = { version = "0.14", features = ["full"] } tokio = { version = "1", features = ["full"] } diff --git a/bins/nittei/src/main.rs b/bins/nittei/src/main.rs index 81552f12..efc9903e 100644 --- a/bins/nittei/src/main.rs +++ b/bins/nittei/src/main.rs @@ -1,5 +1,6 @@ mod telemetry; +use axum::{routing::get, Router}; use nittei_api::Application; use nittei_infra::setup_context; use telemetry::init_subscriber; diff --git a/bins/nittei/tests/api.rs b/bins/nittei/tests/api.rs index f7cdf023..4410585e 100644 --- a/bins/nittei/tests/api.rs +++ b/bins/nittei/tests/api.rs @@ -26,15 +26,13 @@ use nittei_sdk::{ ID, }; -#[actix_web::main] -#[test] +#[tokio::test] async fn test_status_ok() { let (_, sdk, _) = spawn_app().await; assert!(sdk.status.check_health().await.is_ok()); } -#[actix_web::main] -#[test] +#[tokio::test] async fn test_create_account() { let (app, sdk, _) = spawn_app().await; assert!(sdk @@ -44,8 +42,7 @@ async fn test_create_account() { .is_ok()); } -#[actix_web::main] -#[test] +#[tokio::test] async fn test_get_account() { let (app, sdk, address) = spawn_app().await; let res = sdk @@ -59,8 +56,7 @@ async fn test_get_account() { assert!(sdk.account.get().await.is_err()); } -#[actix_web::main] -#[test] +#[tokio::test] async fn test_crud_user() { let (app, sdk, address) = spawn_app().await; let res = sdk @@ -135,8 +131,7 @@ async fn test_crud_user() { assert!(admin_client.user.get(res.user.id.clone()).await.is_err()); } -#[actix_web::main] -#[test] +#[tokio::test] async fn test_user_provide_id() { let (app, sdk, address) = spawn_app().await; let res = sdk @@ -172,8 +167,7 @@ async fn test_user_provide_id() { ); } -#[actix_web::main] -#[test] +#[tokio::test] async fn test_crud_schedule() { let (app, sdk, address) = spawn_app().await; let res = sdk @@ -243,8 +237,7 @@ async fn test_crud_schedule() { .is_err()); } -#[actix_web::main] -#[test] +#[tokio::test] async fn test_create_user() { let (app, sdk, address) = spawn_app().await; let res = sdk @@ -280,8 +273,7 @@ async fn test_create_user() { assert!(get_user_res.is_err()); } -#[actix_web::main] -#[test] +#[tokio::test] async fn test_crud_account() { let (app, sdk, address) = spawn_app().await; let res = sdk @@ -336,8 +328,7 @@ async fn test_crud_account() { assert!(account.account.settings.webhook.is_none()); } -#[actix_web::main] -#[test] +#[tokio::test] async fn test_crud_calendars() { let (app, sdk, address) = spawn_app().await; let res = sdk @@ -444,8 +435,7 @@ async fn test_crud_calendars() { .is_err()); } -#[actix_web::main] -#[test] +#[tokio::test] async fn test_crud_events() { let (app, sdk, address) = spawn_app().await; let res = sdk @@ -567,8 +557,7 @@ async fn test_crud_events() { assert!(admin_client.event.get(event.id.clone()).await.is_err()) } -#[actix_web::main] -#[test] +#[tokio::test] async fn test_crud_service() { let (app, sdk, address) = spawn_app().await; let res = sdk @@ -695,8 +684,7 @@ async fn test_crud_service() { assert!(admin_client.service.get(service.id.clone()).await.is_err()); } -#[actix_web::main] -#[test] +#[tokio::test] async fn test_freebusy_multiple() { let (app, sdk, address) = spawn_app().await; let res = sdk diff --git a/crates/api/Cargo.toml b/crates/api/Cargo.toml index d305c5a4..d59c739d 100644 --- a/crates/api/Cargo.toml +++ b/crates/api/Cargo.toml @@ -16,8 +16,9 @@ nittei_utils = { workspace = true } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" futures = "0.3" -actix-web = "4.8" -actix-cors = "0.7" +axum = "0.5" +tower = "0.4" +hyper = { version = "0.14", features = ["full"] } actix-web-validator = "6.0.0" validator = { version = "0.18", features = ["derive"] } awc = "3.5" @@ -30,7 +31,6 @@ jsonwebtoken = "7" thiserror = "1.0" tokio = { version = "1.0", features = ["full"] } tracing = "0.1.25" -tracing-actix-web = { version = "0.7.11", features = ["opentelemetry_0_23"] } tracing-futures = "0.2.5" [dev-dependencies] diff --git a/crates/api/src/account/mod.rs b/crates/api/src/account/mod.rs index c702af24..6d2ceffb 100644 --- a/crates/api/src/account/mod.rs +++ b/crates/api/src/account/mod.rs @@ -6,8 +6,11 @@ mod remove_account_integration; mod set_account_pub_key; mod set_account_webhook; -use actix_web::web; use add_account_integration::add_account_integration_controller; +use axum::{ + routing::{delete, get, post, put}, + Router, +}; use create_account::create_account_controller; use delete_account_webhook::delete_account_webhook_controller; use get_account::get_account_controller; @@ -16,40 +19,29 @@ use set_account_pub_key::set_account_pub_key_controller; use set_account_webhook::set_account_webhook_controller; /// Configure the routes for the account module -pub fn configure_routes(cfg: &mut web::ServiceConfig) { - // Create a new account - cfg.route("/account", web::post().to(create_account_controller)); - - // Get the account details - cfg.route("/account", web::get().to(get_account_controller)); - - // Set the public key for the account - cfg.route( - "/account/pubkey", - web::put().to(set_account_pub_key_controller), - ); - - // Set the webhook for the account - cfg.route( - "/account/webhook", - web::put().to(set_account_webhook_controller), - ); - - // Delete the webhook for the account - cfg.route( - "/account/webhook", - web::delete().to(delete_account_webhook_controller), - ); - - // Add an integration for the account - cfg.route( - "/account/integration", - web::put().to(add_account_integration_controller), - ); - - // Remove an integration for the account - cfg.route( - "/account/integration/{provider}", - web::delete().to(remove_account_integration_controller), - ); +pub fn configure_routes() -> Router { + Router::new() + // Create a new account + .route("/account", post(create_account_controller)) + // Get the account details + .route("/account", get(get_account_controller)) + // Set the public key for the account + .route("/account/pubkey", put(set_account_pub_key_controller)) + // Set the webhook for the account + .route("/account/webhook", put(set_account_webhook_controller)) + // Delete the webhook for the account + .route( + "/account/webhook", + delete(delete_account_webhook_controller), + ) + // Add an integration for the account + .route( + "/account/integration", + put(add_account_integration_controller), + ) + // Remove an integration for the account + .route( + "/account/integration/:provider", + delete(remove_account_integration_controller), + ) } diff --git a/crates/api/src/calendar/mod.rs b/crates/api/src/calendar/mod.rs index c9c0338e..d640ec9c 100644 --- a/crates/api/src/calendar/mod.rs +++ b/crates/api/src/calendar/mod.rs @@ -1,4 +1,7 @@ -use actix_web::web; +use axum::{ + routing::{delete, get, post, put}, + Router, +}; mod add_sync_calendar; mod create_calendar; @@ -30,107 +33,80 @@ use remove_sync_calendar::remove_sync_calendar_admin_controller; use update_calendar::{update_calendar_admin_controller, update_calendar_controller}; /// Configure the routes for the calendar module -pub fn configure_routes(cfg: &mut web::ServiceConfig) { - // Create a calendar - cfg.route("/calendar", web::post().to(create_calendar_controller)); - // Create a calendar for a user (admin route) - cfg.route( - "/user/{user_id}/calendar", - web::post().to(create_calendar_admin_controller), - ); - - // List calendars - cfg.route( - "/calendar", - web::get().to(get_calendars::get_calendars_controller), - ); - // List calendars for a user (admin route) - cfg.route( - "/user/{user_id}/calendar", - web::get().to(get_calendars::get_calendars_admin_controller), - ); - - // List calendars by metadata - cfg.route( - "/calendar/meta", - web::get().to(get_calendars_by_meta_controller), - ); - - // Get a specific calendar by uid - cfg.route( - "/calendar/{calendar_id}", - web::get().to(get_calendar_controller), - ); - // Get a specific calendar by uid for a user (admin route) - cfg.route( - "/user/calendar/{calendar_id}", - web::get().to(get_calendar_admin_controller), - ); - - // Delete a calendar by uid - cfg.route( - "/calendar/{calendar_id}", - web::delete().to(delete_calendar_controller), - ); - // Delete a calendar by uid for a user (admin route) - cfg.route( - "/user/calendar/{calendar_id}", - web::delete().to(delete_calendar_admin_controller), - ); - - // Update a calendar by uid - cfg.route( - "/calendar/{calendar_id}", - web::put().to(update_calendar_controller), - ); - // Update a calendar by uid for a user (admin route) - cfg.route( - "/user/calendar/{calendar_id}", - web::put().to(update_calendar_admin_controller), - ); - - // Get events for a calendar - cfg.route( - "/calendar/{calendar_id}/events", - web::get().to(get_calendar_events_controller), - ); - // Get events for a calendar for a user (admin route) - cfg.route( - "/user/calendar/{calendar_id}/events", - web::get().to(get_calendar_events_admin_controller), - ); - - // Calendar providers - cfg.route( - "/calendar/provider/google", - web::get().to(get_google_calendars_controller), - ); - cfg.route( - "/user/{user_id}/calendar/provider/google", - web::get().to(get_google_calendars_admin_controller), - ); - cfg.route( - "/calendar/provider/outlook", - web::get().to(get_outlook_calendars_controller), - ); - cfg.route( - "/user/{user_id}/calendar/provider/outlook", - web::get().to(get_outlook_calendars_admin_controller), - ); - // cfg.route( - // "/calendar/sync/", - // web::put().to(add_sync_calendar_controller), - // ); - cfg.route( - "/user/{user_id}/calendar/sync", - web::put().to(add_sync_calendar_admin_controller), - ); - // cfg.route( - // "/calendar/sync", - // web::delete().to(remove_sync_calendar_controller), - // ); - cfg.route( - "/user/{user_id}/calendar/sync", - web::delete().to(remove_sync_calendar_admin_controller), - ); +pub fn configure_routes() -> Router { + Router::new() + // Create a calendar + .route("/calendar", post(create_calendar_controller)) + // Create a calendar for a user (admin route) + .route( + "/user/:user_id/calendar", + post(create_calendar_admin_controller), + ) + // List calendars + .route("/calendar", get(get_calendars::get_calendars_controller)) + // List calendars for a user (admin route) + .route( + "/user/:user_id/calendar", + get(get_calendars::get_calendars_admin_controller), + ) + // List calendars by metadata + .route("/calendar/meta", get(get_calendars_by_meta_controller)) + // Get a specific calendar by uid + .route("/calendar/:calendar_id", get(get_calendar_controller)) + // Get a specific calendar by uid for a user (admin route) + .route( + "/user/calendar/:calendar_id", + get(get_calendar_admin_controller), + ) + // Delete a calendar by uid + .route("/calendar/:calendar_id", delete(delete_calendar_controller)) + // Delete a calendar by uid for a user (admin route) + .route( + "/user/calendar/:calendar_id", + delete(delete_calendar_admin_controller), + ) + // Update a calendar by uid + .route("/calendar/:calendar_id", put(update_calendar_controller)) + // Update a calendar by uid for a user (admin route) + .route( + "/user/calendar/:calendar_id", + put(update_calendar_admin_controller), + ) + // Get events for a calendar + .route( + "/calendar/:calendar_id/events", + get(get_calendar_events_controller), + ) + // Get events for a calendar for a user (admin route) + .route( + "/user/calendar/:calendar_id/events", + get(get_calendar_events_admin_controller), + ) + // Calendar providers + .route( + "/calendar/provider/google", + get(get_google_calendars_controller), + ) + .route( + "/user/:user_id/calendar/provider/google", + get(get_google_calendars_admin_controller), + ) + .route( + "/calendar/provider/outlook", + get(get_outlook_calendars_controller), + ) + .route( + "/user/:user_id/calendar/provider/outlook", + get(get_outlook_calendars_admin_controller), + ) + // .route("/calendar/sync/", put(add_sync_calendar_controller)) + .route( + "/user/:user_id/calendar/sync", + put(add_sync_calendar_admin_controller), + ) + // .route("/calendar/sync", delete(remove_sync_calendar_controller)) + .route( + "/user/:user_id/calendar/sync", + delete(remove_sync_calendar_admin_controller), + ) } diff --git a/crates/api/src/event/mod.rs b/crates/api/src/event/mod.rs index f7777334..41caf189 100644 --- a/crates/api/src/event/mod.rs +++ b/crates/api/src/event/mod.rs @@ -11,7 +11,10 @@ mod subscribers; pub mod sync_event_reminders; mod update_event; -use actix_web::web; +use axum::{ + routing::{delete, get, post, put}, + Router, +}; use create_event::{create_event_admin_controller, create_event_controller}; use delete_event::{delete_event_admin_controller, delete_event_controller}; use get_event::{get_event_admin_controller, get_event_controller}; @@ -21,69 +24,50 @@ use search_events::search_events_controller; use update_event::{update_event_admin_controller, update_event_controller}; // Configure the routes for the event module -pub fn configure_routes(cfg: &mut web::ServiceConfig) { - // Create an event - cfg.route("/events", web::post().to(create_event_controller)); - // Create an event for a user (admin route) - cfg.route( - "/user/{user_id}/events", - web::post().to(create_event_admin_controller), - ); - - // Get events by calendars - cfg.route( - "/user/{user_id}/events", - web::get().to(get_events_by_calendars::get_events_by_calendars_controller), - ); - - // Get events by metadata - cfg.route("/events/meta", web::get().to(get_events_by_meta_controller)); - - // Search events - // /!\ This is a POST route - cfg.route("/events/search", web::post().to(search_events_controller)); - - // Get a specific event by external id - cfg.route( - "/user/events/external_id/{external_id}", - web::get().to(get_event_by_external_id::get_event_by_external_id_admin_controller), - ); - - // Get a specific event by uid - cfg.route("/events/{event_id}", web::get().to(get_event_controller)); - // Get a specific event by uid (admin route) - cfg.route( - "/user/events/{event_id}", - web::get().to(get_event_admin_controller), - ); - - // Delete an event by uid - cfg.route( - "/events/{event_id}", - web::delete().to(delete_event_controller), - ); - // Delete an event by uid (admin route) - cfg.route( - "/user/events/{event_id}", - web::delete().to(delete_event_admin_controller), - ); - - // Update an event by uid - cfg.route("/events/{event_id}", web::put().to(update_event_controller)); - // Update an event by uid (admin route) - cfg.route( - "/user/events/{event_id}", - web::put().to(update_event_admin_controller), - ); - - // Get event instances - cfg.route( - "/events/{event_id}/instances", - web::get().to(get_event_instances_controller), - ); - // Get event instances (admin route) - cfg.route( - "/user/events/{event_id}/instances", - web::get().to(get_event_instances_admin_controller), - ); +pub fn configure_routes() -> Router { + Router::new() + // Create an event + .route("/events", post(create_event_controller)) + // Create an event for a user (admin route) + .route("/user/:user_id/events", post(create_event_admin_controller)) + // Get events by calendars + .route( + "/user/:user_id/events", + get(get_events_by_calendars::get_events_by_calendars_controller), + ) + // Get events by metadata + .route("/events/meta", get(get_events_by_meta_controller)) + // Search events + // /!\ This is a POST route + .route("/events/search", post(search_events_controller)) + // Get a specific event by external id + .route( + "/user/events/external_id/:external_id", + get(get_event_by_external_id::get_event_by_external_id_admin_controller), + ) + // Get a specific event by uid + .route("/events/:event_id", get(get_event_controller)) + // Get a specific event by uid (admin route) + .route("/user/events/:event_id", get(get_event_admin_controller)) + // Delete an event by uid + .route("/events/:event_id", delete(delete_event_controller)) + // Delete an event by uid (admin route) + .route( + "/user/events/:event_id", + delete(delete_event_admin_controller), + ) + // Update an event by uid + .route("/events/:event_id", put(update_event_controller)) + // Update an event by uid (admin route) + .route("/user/events/:event_id", put(update_event_admin_controller)) + // Get event instances + .route( + "/events/:event_id/instances", + get(get_event_instances_controller), + ) + // Get event instances (admin route) + .route( + "/user/events/:event_id/instances", + get(get_event_instances_admin_controller), + ) } diff --git a/crates/api/src/event_group/mod.rs b/crates/api/src/event_group/mod.rs index a92110cb..0fc1bdef 100644 --- a/crates/api/src/event_group/mod.rs +++ b/crates/api/src/event_group/mod.rs @@ -4,7 +4,10 @@ mod get_event_group; mod get_event_group_by_external_id; mod update_event_group; -use actix_web::web; +use axum::{ + routing::{delete, get, post, put}, + Router, +}; use create_event_group::create_event_group_admin_controller; use delete_event_group::delete_event_group_admin_controller; use get_event_group::get_event_group_admin_controller; @@ -12,34 +15,31 @@ use get_event_group_by_external_id::get_event_group_by_external_id_admin_control use update_event_group::update_event_group_admin_controller; // Configure the routes for the event_group module -pub fn configure_routes(cfg: &mut web::ServiceConfig) { - // Create an event group for a user (admin route) - cfg.route( - "/user/{user_id}/event_groups", - web::post().to(create_event_group_admin_controller), - ); - - // Get a specific event group by external id - cfg.route( - "/user/event_groups/external_id/{external_id}", - web::get().to(get_event_group_by_external_id_admin_controller), - ); - - // Get a specific event group by uid (admin route) - cfg.route( - "/user/event_groups/{event_group_id}", - web::get().to(get_event_group_admin_controller), - ); - - // Update an event group by uid (admin route) - cfg.route( - "/user/event_groups/{event_group_id}", - web::put().to(update_event_group_admin_controller), - ); - - // Delete an event group by uid (admin route) - cfg.route( - "/user/event_groups/{event_group_id}", - web::delete().to(delete_event_group_admin_controller), - ); +pub fn configure_routes() -> Router { + Router::new() + // Create an event group for a user (admin route) + .route( + "/user/:user_id/event_groups", + post(create_event_group_admin_controller), + ) + // Get a specific event group by external id + .route( + "/user/event_groups/external_id/:external_id", + get(get_event_group_by_external_id_admin_controller), + ) + // Get a specific event group by uid (admin route) + .route( + "/user/event_groups/:event_group_id", + get(get_event_group_admin_controller), + ) + // Update an event group by uid (admin route) + .route( + "/user/event_groups/:event_group_id", + put(update_event_group_admin_controller), + ) + // Delete an event group by uid (admin route) + .route( + "/user/event_groups/:event_group_id", + delete(delete_event_group_admin_controller), + ) } diff --git a/crates/api/src/http_logger.rs b/crates/api/src/http_logger.rs index 1e0cdc9c..8b627415 100644 --- a/crates/api/src/http_logger.rs +++ b/crates/api/src/http_logger.rs @@ -1,79 +1,76 @@ -use actix_web::{ - body::MessageBody, - dev::{ServiceRequest, ServiceResponse}, - Error, +use axum::{ + body::Body, + http::{Request, Response}, }; +use tower_http::trace::{DefaultMakeSpan, DefaultOnResponse, TraceLayer}; use tracing::{Level, Span}; -use tracing_actix_web::{DefaultRootSpanBuilder, RootSpanBuilder}; -/// Custom root span builder (tracing) for Actix Web +/// Custom root span builder (tracing) for Axum pub struct NitteiTracingRootSpanBuilder; -impl RootSpanBuilder for NitteiTracingRootSpanBuilder { +impl NitteiTracingRootSpanBuilder { /// Create a new root span for the incoming request - fn on_request_start(request: &ServiceRequest) -> Span { + pub fn on_request_start(request: &Request) -> Span { // Ignore healthcheck endpoint - let level = if request.path() == "/api/v1/healthcheck" { + let level = if request.uri().path() == "/api/v1/healthcheck" { Level::DEBUG } else { Level::INFO }; - tracing_actix_web::root_span!(level = level, request) + tracing::span!(level, "request", method = %request.method(), uri = %request.uri()) } /// End the root span for the incoming request - fn on_request_end(span: Span, outcome: &Result, Error>) { + pub fn on_request_end(span: Span, response: &Response) { // Log the outcome of the request - log_request(outcome); + log_request(response); - DefaultRootSpanBuilder::on_request_end(span, outcome); + span.record("status", &tracing::field::display(response.status())); } } /// Log the outcome of the request -fn log_request(outcome: &Result, Error>) { +fn log_request(response: &Response) { // Log the outcome of the request - if let Ok(response) = outcome { - let status_code = response.status().as_u16(); - let method = response.request().method().to_string(); - let path = response.request().path().to_string(); + let status_code = response.status().as_u16(); + let method = response.request().method().to_string(); + let path = response.request().uri().path().to_string(); - // Ignore healthcheck endpoint - if path == "/api/v1/healthcheck" { - return; - } + // Ignore healthcheck endpoint + if path == "/api/v1/healthcheck" { + return; + } - // Log with custom fields in JSON format - let message = format!("{} {} => {}", method, path, status_code); + // Log with custom fields in JSON format + let message = format!("{} {} => {}", method, path, status_code); - if status_code >= 500 { - tracing::error!( - method = method, - path = path, - status_code = status_code, - message, - ); - } else if status_code >= 400 { - tracing::warn!( - method = method, - path = path, - status_code = status_code, - message, - ); - } else { - tracing::info!( - method = method, - path = path, - status_code = status_code, - message, - ); - }; - } else if let Err(err) = outcome { - // Fallback in case we can't retrieve the request from the span + if status_code >= 500 { tracing::error!( - status_code = 500, - error = %err, - "HTTP request resulted in an error, but request details are missing" + method = method, + path = path, + status_code = status_code, + message, ); - } + } else if status_code >= 400 { + tracing::warn!( + method = method, + path = path, + status_code = status_code, + message, + ); + } else { + tracing::info!( + method = method, + path = path, + status_code = status_code, + message, + ); + }; +} + +/// Create a TraceLayer for Axum with custom root span builder +pub fn create_trace_layer() -> TraceLayer { + TraceLayer::new_for_http() + .make_span_with(NitteiTracingRootSpanBuilder::on_request_start) + .on_response(NitteiTracingRootSpanBuilder::on_request_end) } diff --git a/crates/api/src/lib.rs b/crates/api/src/lib.rs index 99384cd6..f441ba18 100644 --- a/crates/api/src/lib.rs +++ b/crates/api/src/lib.rs @@ -11,16 +11,9 @@ mod shared; mod status; mod user; -use std::{net::TcpListener, sync::Arc}; - -use actix_cors::Cors; -use actix_web::{ - dev::Server, - middleware::{self}, - web::{self, Data}, - App, - HttpServer, -}; +use std::{net::SocketAddr, sync::Arc}; + +use axum::{routing::get, Extension, Router, Server}; use futures::lock::Mutex; use http_logger::NitteiTracingRootSpanBuilder; use job_schedulers::{start_reminder_generation_job, start_send_reminders_job}; @@ -33,25 +26,26 @@ use nittei_domain::{ ID, }; use nittei_infra::NitteiContext; +use tower_http::trace::TraceLayer; use tracing::{error, info, warn}; -use tracing_actix_web::TracingLogger; -/// Configure the Actix server API +/// Configure the Axum server API /// Add all the routes to the server -pub fn configure_server_api(cfg: &mut web::ServiceConfig) { - account::configure_routes(cfg); - calendar::configure_routes(cfg); - event::configure_routes(cfg); - event_group::configure_routes(cfg); - schedule::configure_routes(cfg); - service::configure_routes(cfg); - status::configure_routes(cfg); - user::configure_routes(cfg); +pub fn configure_server_api() -> Router { + Router::new() + .nest("/account", account::configure_routes()) + .nest("/calendar", calendar::configure_routes()) + .nest("/event", event::configure_routes()) + .nest("/event_group", event_group::configure_routes()) + .nest("/schedule", schedule::configure_routes()) + .nest("/service", service::configure_routes()) + .nest("/status", status::configure_routes()) + .nest("/user", user::configure_routes()) } /// Struct for storing the main application state pub struct Application { - /// The Actix server instance + /// The Axum server instance server: Server, /// The port the server is running on port: u16, @@ -103,7 +97,7 @@ impl Application { } } - /// Configure the Actix server + /// Configure the Axum server /// This function creates the server and adds all the routes to it /// /// This adds the following middleware: @@ -118,35 +112,24 @@ impl Application { let address = nittei_utils::config::APP_CONFIG.http_host.clone(); let address_and_port = format!("{}:{}", address, port); info!("Starting server on: {}", address_and_port); - let listener = TcpListener::bind(address_and_port)?; - let port = listener.local_addr()?.port(); - - let server = HttpServer::new(move || { - let ctx = context.clone(); - let shared_state = shared_state.clone(); - - App::new() - .wrap(Cors::permissive()) - .wrap(middleware::Compress::default()) - .wrap(TracingLogger::::new()) - .app_data(Data::new(ctx)) - .app_data(Data::new(shared_state)) - .service(web::scope("/api/v1").configure(configure_server_api)) - }) - // Disable signals to avoid conflicts with the signal handler - // This is handled by the signal handler in the binary and the `schedule_shutdown` function - .disable_signals() - // Set the shutdown timeout (time to wait for the server to finish processing requests) - // Default is 30 seconds - .shutdown_timeout(nittei_utils::config::APP_CONFIG.server_shutdown_timeout) - .listen(listener)? - .workers(4) - .run(); + let addr: SocketAddr = address_and_port.parse()?; + let port = addr.port(); + + let app = Router::new() + .route("/", get(|| async { "Hello, World!" })) + .layer(TraceLayer::new_for_http()) + .layer(Extension(context.clone())) + .layer(Extension(shared_state.clone())) + .nest("/api/v1", configure_server_api()); + + let server = Server::bind(&addr) + .serve(app.into_make_service()) + .with_graceful_shutdown(shutdown_signal()); Ok((server, port)) } - /// Init the default account and start the Actix server + /// Init the default account and start the Axum server /// /// It also sets up the shutdown handler pub async fn start( @@ -316,7 +299,7 @@ impl Application { /// Setup the shutdown handler fn setup_shutdown_handler(&self, shutdown_channel: tokio::sync::oneshot::Receiver<()>) { - let server_handle = self.server.handle(); + let server_handle = self.server.clone(); let shared_state = self.shared_state.clone(); // Listen to shutdown channel @@ -330,7 +313,7 @@ impl Application { if cfg!(debug_assertions) { // In debug mode, stop the server immediately info!("[server] Stopping server..."); - server_handle.stop(true).await; + server_handle.abort(); info!("[server] Server stopped"); } else { // In production, do the whole graceful shutdown process @@ -350,7 +333,7 @@ impl Application { info!("[server] Stopping server..."); // Shutdown the server - server_handle.stop(true).await; + server_handle.abort(); info!("[server] Server stopped"); } @@ -358,3 +341,8 @@ impl Application { }); } } + +async fn shutdown_signal() { + // Wait for the shutdown signal + let _ = tokio::signal::ctrl_c().await; +} diff --git a/crates/api/src/schedule/mod.rs b/crates/api/src/schedule/mod.rs index b0bfe25f..37cee8de 100644 --- a/crates/api/src/schedule/mod.rs +++ b/crates/api/src/schedule/mod.rs @@ -4,49 +4,37 @@ mod get_schedule; mod get_schedules_by_meta; mod update_schedule; -use actix_web::web; +use axum::{ + routing::{delete, get, post, put}, + Router, +}; use create_schedule::{create_schedule_admin_controller, create_schedule_controller}; use delete_schedule::{delete_schedule_admin_controller, delete_schedule_controller}; use get_schedule::{get_schedule_admin_controller, get_schedule_controller}; use get_schedules_by_meta::get_schedules_by_meta_controller; use update_schedule::{update_schedule_admin_controller, update_schedule_controller}; -pub fn configure_routes(cfg: &mut web::ServiceConfig) { - cfg.route("/schedule", web::post().to(create_schedule_controller)); - cfg.route( - "/user/{user_id}/schedule", - web::post().to(create_schedule_admin_controller), - ); - - cfg.route( - "/schedule/meta", - web::get().to(get_schedules_by_meta_controller), - ); - - cfg.route( - "/schedule/{schedule_id}", - web::get().to(get_schedule_controller), - ); - cfg.route( - "/user/schedule/{schedule_id}", - web::get().to(get_schedule_admin_controller), - ); - - cfg.route( - "/schedule/{schedule_id}", - web::delete().to(delete_schedule_controller), - ); - cfg.route( - "/user/schedule/{schedule_id}", - web::delete().to(delete_schedule_admin_controller), - ); - - cfg.route( - "/schedule/{schedule_id}", - web::put().to(update_schedule_controller), - ); - cfg.route( - "/user/schedule/{schedule_id}", - web::put().to(update_schedule_admin_controller), - ); +pub fn configure_routes() -> Router { + Router::new() + .route("/schedule", post(create_schedule_controller)) + .route( + "/user/:user_id/schedule", + post(create_schedule_admin_controller), + ) + .route("/schedule/meta", get(get_schedules_by_meta_controller)) + .route("/schedule/:schedule_id", get(get_schedule_controller)) + .route( + "/user/schedule/:schedule_id", + get(get_schedule_admin_controller), + ) + .route("/schedule/:schedule_id", delete(delete_schedule_controller)) + .route( + "/user/schedule/:schedule_id", + delete(delete_schedule_admin_controller), + ) + .route("/schedule/:schedule_id", put(update_schedule_controller)) + .route( + "/user/schedule/:schedule_id", + put(update_schedule_admin_controller), + ) } diff --git a/crates/api/src/service/mod.rs b/crates/api/src/service/mod.rs index 69631ce6..5dc8f8d6 100644 --- a/crates/api/src/service/mod.rs +++ b/crates/api/src/service/mod.rs @@ -12,9 +12,12 @@ mod remove_user_from_service; mod update_service; mod update_service_user; -use actix_web::web; use add_busy_calendar::add_busy_calendar_controller; use add_user_to_service::add_user_to_service_controller; +use axum::{ + routing::{delete, get, post, put}, + Router, +}; use create_service::create_service_controller; use create_service_event_intend::create_service_event_intend_controller; use delete_service::delete_service_controller; @@ -27,54 +30,43 @@ use remove_user_from_service::remove_user_from_service_controller; use update_service::update_service_controller; use update_service_user::update_service_user_controller; -pub fn configure_routes(cfg: &mut web::ServiceConfig) { - cfg.route("/service", web::post().to(create_service_controller)); - cfg.route( - "/service/meta", - web::get().to(get_services_by_meta_controller), - ); - cfg.route( - "/service/{service_id}", - web::get().to(get_service_controller), - ); - cfg.route( - "/service/{service_id}", - web::put().to(update_service_controller), - ); - cfg.route( - "/service/{service_id}", - web::delete().to(delete_service_controller), - ); - cfg.route( - "/service/{service_id}/users", - web::post().to(add_user_to_service_controller), - ); - cfg.route( - "/service/{service_id}/users/{user_id}", - web::delete().to(remove_user_from_service_controller), - ); - cfg.route( - "/service/{service_id}/users/{user_id}", - web::put().to(update_service_user_controller), - ); - cfg.route( - "/service/{service_id}/users/{user_id}/busy", - web::put().to(add_busy_calendar_controller), - ); - cfg.route( - "/service/{service_id}/users/{user_id}/busy", - web::delete().to(remove_busy_calendar_controller), - ); - cfg.route( - "/service/{service_id}/booking", - web::get().to(get_service_bookingslots_controller), - ); - cfg.route( - "/service/{service_id}/booking-intend", - web::post().to(create_service_event_intend_controller), - ); - cfg.route( - "/service/{service_id}/booking-intend", - web::delete().to(remove_service_event_intend_controller), - ); +pub fn configure_routes() -> Router { + Router::new() + .route("/service", post(create_service_controller)) + .route("/service/meta", get(get_services_by_meta_controller)) + .route("/service/:service_id", get(get_service_controller)) + .route("/service/:service_id", put(update_service_controller)) + .route("/service/:service_id", delete(delete_service_controller)) + .route( + "/service/:service_id/users", + post(add_user_to_service_controller), + ) + .route( + "/service/:service_id/users/:user_id", + delete(remove_user_from_service_controller), + ) + .route( + "/service/:service_id/users/:user_id", + put(update_service_user_controller), + ) + .route( + "/service/:service_id/users/:user_id/busy", + put(add_busy_calendar_controller), + ) + .route( + "/service/:service_id/users/:user_id/busy", + delete(remove_busy_calendar_controller), + ) + .route( + "/service/:service_id/booking", + get(get_service_bookingslots_controller), + ) + .route( + "/service/:service_id/booking-intend", + post(create_service_event_intend_controller), + ) + .route( + "/service/:service_id/booking-intend", + delete(remove_service_event_intend_controller), + ) } diff --git a/crates/api/src/status/mod.rs b/crates/api/src/status/mod.rs index 7602d726..b59f7f91 100644 --- a/crates/api/src/status/mod.rs +++ b/crates/api/src/status/mod.rs @@ -1,4 +1,6 @@ -use actix_web::{web, HttpResponse}; +use std::sync::Arc; + +use axum::{extract::Extension, http::StatusCode, response::IntoResponse, routing::get, Router}; use nittei_api_structs::get_service_health::*; use nittei_infra::NitteiContext; @@ -6,29 +8,37 @@ use crate::ServerSharedState; /// Get the status of the service async fn status( - ctx: web::Data, - shared_state: web::Data, -) -> HttpResponse { + Extension(ctx): Extension>, + Extension(shared_state): Extension>, +) -> impl IntoResponse { let is_shutting_down = shared_state.is_shutting_down.lock().await; if *is_shutting_down { - return HttpResponse::ServiceUnavailable().json(APIResponse { - message: "Service is shutting down".into(), - }); + return ( + StatusCode::SERVICE_UNAVAILABLE, + axum::Json(APIResponse { + message: "Service is shutting down".into(), + }), + ); } match ctx.repos.status.check_connection().await { - Ok(_) => HttpResponse::Ok().json(APIResponse { - message: "Ok!\r\n".into(), - }), - Err(_) => HttpResponse::InternalServerError().json(APIResponse { - message: "Internal Server Error".into(), - }), + Ok(_) => ( + StatusCode::OK, + axum::Json(APIResponse { + message: "Ok!\r\n".into(), + }), + ), + Err(_) => ( + StatusCode::INTERNAL_SERVER_ERROR, + axum::Json(APIResponse { + message: "Internal Server Error".into(), + }), + ), } } /// Configure the routes for the status module -pub fn configure_routes(cfg: &mut web::ServiceConfig) { - // Get the health status of the service - cfg.route("/healthcheck", web::get().to(status)); +pub fn configure_routes() -> Router { + Router::new().route("/healthcheck", get(status)) } diff --git a/crates/api/src/user/mod.rs b/crates/api/src/user/mod.rs index f9fadf4a..fef1d11d 100644 --- a/crates/api/src/user/mod.rs +++ b/crates/api/src/user/mod.rs @@ -10,7 +10,10 @@ mod oauth_integration; mod remove_integration; mod update_user; -use actix_web::web; +use axum::{ + routing::{delete, get, post, put}, + Router, +}; use create_user::create_user_controller; use delete_user::delete_user_controller; use get_me::get_me_controller; @@ -25,56 +28,39 @@ use remove_integration::{remove_integration_admin_controller, remove_integration use update_user::update_user_controller; // Configure the routes for the user module -pub fn configure_routes(cfg: &mut web::ServiceConfig) { - // Create a new user - cfg.route("/user", web::post().to(create_user_controller)); - - // Get the current user - cfg.route("/me", web::get().to(get_me_controller)); - - // Get users by metadata - cfg.route("/user/meta", web::get().to(get_users_by_meta_controller)); - - // Get user by external_id - cfg.route( - "/user/external_id/{external_id}", - web::get().to(get_user_by_external_id_controller), - ); - - // Get freebusy for multiple users - // This is a POST route ! - cfg.route( - "/user/freebusy", - web::post().to(get_multiple_freebusy_controller), - ); - - // Get a specific user by id - cfg.route("/user/{user_id}", web::get().to(get_user_controller)); - - // Update a specific user by id - cfg.route("/user/{user_id}", web::put().to(update_user_controller)); - - // Delete a specific user by id - cfg.route("/user/{user_id}", web::delete().to(delete_user_controller)); - - // Get freebusy for a specific user - cfg.route( - "/user/{user_id}/freebusy", - web::get().to(get_freebusy_controller), - ); - - // Oauth - cfg.route("/me/oauth", web::post().to(oauth_integration_controller)); - cfg.route( - "/me/oauth/{provider}", - web::delete().to(remove_integration_controller), - ); - cfg.route( - "/user/{user_id}/oauth", - web::post().to(oauth_integration_admin_controller), - ); - cfg.route( - "/user/{user_id}/oauth/{provider}", - web::delete().to(remove_integration_admin_controller), - ); +pub fn configure_routes() -> Router { + Router::new() + // Create a new user + .route("/user", post(create_user_controller)) + // Get the current user + .route("/me", get(get_me_controller)) + // Get users by metadata + .route("/user/meta", get(get_users_by_meta_controller)) + // Get user by external_id + .route( + "/user/external_id/:external_id", + get(get_user_by_external_id_controller), + ) + // Get freebusy for multiple users + // This is a POST route ! + .route("/user/freebusy", post(get_multiple_freebusy_controller)) + // Get a specific user by id + .route("/user/:user_id", get(get_user_controller)) + // Update a specific user by id + .route("/user/:user_id", put(update_user_controller)) + // Delete a specific user by id + .route("/user/:user_id", delete(delete_user_controller)) + // Get freebusy for a specific user + .route("/user/:user_id/freebusy", get(get_freebusy_controller)) + // Oauth + .route("/me/oauth", post(oauth_integration_controller)) + .route("/me/oauth/:provider", delete(remove_integration_controller)) + .route( + "/user/:user_id/oauth", + post(oauth_integration_admin_controller), + ) + .route( + "/user/:user_id/oauth/:provider", + delete(remove_integration_admin_controller), + ) }