Skip to content
Open
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
2,209 changes: 1,336 additions & 873 deletions api/Cargo.lock

Large diffs are not rendered by default.

23 changes: 14 additions & 9 deletions api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,14 @@ datastore-adaptor = []
members = ["common", "adaptors/*"]

[dependencies]
axum = { version = "0.6.18", features = ["headers"] }
axum = "0.8.1"
axum-extra = { version = "0.10.0", features = ["typed-header"] }
serde = { version = "1.0.162", features = ["derive"] }
tokio = { version = "1.28.0", features = ["macros", "rt-multi-thread"] }
tokio = { version = "1.28.0", features = [
"macros",
"rt-multi-thread",
"signal",
] }
common = { path = "common" }
sql-adaptor = { path = "adaptors/sql" }
datastore-adaptor = { path = "adaptors/datastore" }
Expand All @@ -28,10 +33,10 @@ regex = "1.8.1"
tracing = "0.1.37"
tracing-subscriber = "0.3.17"
chrono = "0.4.24"
bcrypt = "0.14.0"
tower-http = { version = "0.4.0", features = ["cors", "trace"] }
tower_governor = "0.0.4"
tower = "0.4.13"
utoipa = { version = "3.3.0", features = ["axum_extras", "preserve_order"] }
utoipa-swagger-ui = { version = "3.1.3", features = ["axum", "debug-embed"] }
base64 = "0.21.0"
bcrypt = "0.16.0"
tower-http = { version = "0.6.2", features = ["cors", "trace"] }
tower_governor = "0.6.0"
tower = "0.5.2"
utoipa = { version = "5.3.1", features = ["axum_extras", "preserve_order"] }
utoipa-swagger-ui = { version = "9.0.0", features = ["axum", "debug-embed"] }
base64 = "0.22.1"
4 changes: 2 additions & 2 deletions api/adaptors/datastore/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::{env, error::Error, fmt::Display};

use async_trait::async_trait;
use chrono::{DateTime, NaiveDateTime, Utc};
use chrono::{DateTime, Utc};
use common::{Adaptor, Event, Person, Stats};
use google_cloud::{
authorize::ApplicationCredentials,
Expand Down Expand Up @@ -297,7 +297,7 @@ impl DatastoreEvent {
}

fn unix_to_date(unix: i64) -> DateTime<Utc> {
DateTime::from_utc(NaiveDateTime::from_timestamp_opt(unix, 0).unwrap(), Utc)
DateTime::from_timestamp(unix, 0).unwrap()
}

#[derive(Debug)]
Expand Down
4 changes: 2 additions & 2 deletions api/adaptors/memory/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ impl Adaptor for MemoryAdaptor {
let state = self.state.lock().await;

// Event doesn't exist
if state.events.get(&event_id).is_none() {
if !state.events.contains_key(&event_id) {
return Ok(None);
}

Expand Down Expand Up @@ -71,7 +71,7 @@ impl Adaptor for MemoryAdaptor {
let mut state = self.state.lock().await;

// Check event exists
if state.events.get(&event_id).is_none() {
if !state.events.contains_key(&event_id) {
return Ok(None);
}

Expand Down
2 changes: 0 additions & 2 deletions api/adaptors/sql/src/entity/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3

pub mod prelude;

pub mod event;
pub mod person;
pub mod stats;
5 changes: 0 additions & 5 deletions api/adaptors/sql/src/entity/prelude.rs

This file was deleted.

6 changes: 3 additions & 3 deletions api/adaptors/sql/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,8 @@ impl From<event::Model> for Event {
Self {
id: value.id,
name: value.name,
created_at: DateTime::<Utc>::from_utc(value.created_at, Utc),
visited_at: DateTime::<Utc>::from_utc(value.visited_at, Utc),
created_at: DateTime::from_naive_utc_and_offset(value.created_at, Utc),
visited_at: DateTime::from_naive_utc_and_offset(value.visited_at, Utc),
times: serde_json::from_value(value.times).unwrap_or(vec![]),
timezone: value.timezone,
}
Expand All @@ -227,7 +227,7 @@ impl From<person::Model> for Person {
Self {
name: value.name,
password_hash: value.password_hash,
created_at: DateTime::<Utc>::from_utc(value.created_at, Utc),
created_at: DateTime::from_naive_utc_and_offset(value.created_at, Utc),
availability: serde_json::from_value(value.availability).unwrap_or(vec![]),
}
}
Expand Down
51 changes: 23 additions & 28 deletions api/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
use std::{env, net::SocketAddr, sync::Arc};

use axum::{
error_handling::HandleErrorLayer,
extract,
http::{
header::{ACCEPT, AUTHORIZATION, CONTENT_TYPE},
HeaderValue, Method,
},
routing::{get, patch, post},
BoxError, Router, Server,
Router,
};
use routes::*;
use tokio::sync::Mutex;
use tower::ServiceBuilder;
use tower_governor::{errors::display_error, governor::GovernorConfigBuilder, GovernorLayer};
use tower_governor::{governor::GovernorConfigBuilder, GovernorLayer};
use tower_http::{cors::CorsLayer, trace::TraceLayer};
use tracing::Level;
use utoipa::OpenApi;
Expand Down Expand Up @@ -63,40 +61,34 @@ async fn main() {
// Rate limiting configuration (using tower_governor)
// From the docs: Allows bursts with up to 20 requests and replenishes
// one element after 500ms, based on peer IP.
let governor_config = Box::new(
let governor_conf = Arc::new(
GovernorConfigBuilder::default()
.burst_size(20)
.finish()
.unwrap(),
);
let rate_limit = ServiceBuilder::new()
// Handle errors from governor and convert into HTTP responses
.layer(HandleErrorLayer::new(|e: BoxError| async move {
display_error(e)
}))
.layer(GovernorLayer {
config: Box::leak(governor_config),
});

let app = Router::new()
.merge(SwaggerUi::new("/docs").url("/docs/openapi.json", ApiDoc::openapi()))
.route("/", get(get_root))
.route("/stats", get(stats::get_stats))
.route("/event", post(event::create_event))
.route("/event/:event_id", get(event::get_event))
.route("/event/:event_id/people", get(person::get_people))
.route("/event/{event_id}", get(event::get_event))
.route("/event/{event_id}/people", get(person::get_people))
.route(
"/event/:event_id/people/:person_name",
"/event/{event_id}/people/{person_name}",
get(person::get_person),
)
.route(
"/event/:event_id/people/:person_name",
"/event/{event_id}/people/{person_name}",
patch(person::update_person),
)
.route("/tasks/cleanup", get(tasks::cleanup))
.with_state(shared_state)
.layer(cors)
.layer(rate_limit)
.layer(GovernorLayer {
config: governor_conf,
})
.with_state(shared_state)
.layer(TraceLayer::new_for_http());

let addr = SocketAddr::from(([0, 0, 0, 0], 3000));
Expand All @@ -110,15 +102,18 @@ async fn main() {
"release"
}
);
Server::bind(&addr)
.serve(app.into_make_service_with_connect_info::<SocketAddr>())
.with_graceful_shutdown(async {
tokio::signal::ctrl_c()
.await
.expect("Failed to install Ctrl+C handler")
})
.await
.unwrap();
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
axum::serve(
listener,
app.into_make_service_with_connect_info::<SocketAddr>(),
)
.with_graceful_shutdown(async {
tokio::signal::ctrl_c()
.await
.expect("Failed to install Ctrl+C handler")
})
.await
.unwrap();
}

async fn get_root() -> String {
Expand Down
5 changes: 4 additions & 1 deletion api/src/routes/person.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use axum::{
extract::{self, Path},
Json,
};
use axum_extra::{
headers::{authorization::Bearer, Authorization},
Json, TypedHeader,
TypedHeader,
};
use base64::{engine::general_purpose, Engine};
use common::{Adaptor, Person};
Expand Down