#![allow(deprecated)]
use actix_tower::compat::tower::TowerLayerCompat;
use actix_tower::middleware::RateLimit;
use actix_tower::prelude::*;
use actix_web::{web, App, FromRequest, HttpRequest, HttpResponse, HttpServer};
use std::future::{ready, Ready};
use std::time::Duration;
use tower_http::timeout::TimeoutLayer;
use tower_http::trace::TraceLayer;
#[derive(Debug, Clone)]
struct AuthenticatedUser {
username: String,
}
impl FromRequest for AuthenticatedUser {
type Error = ApiError;
type Future = Ready<Result<Self, Self::Error>>;
fn from_request(req: &HttpRequest, _payload: &mut actix_web::dev::Payload) -> Self::Future {
if let Some(auth) = req.headers().get("authorization") {
if let Ok(token) = auth.to_str() {
if token == "Bearer secret-admin-token" {
return ready(Ok(AuthenticatedUser {
username: "admin".to_string(),
}));
}
}
}
ready(Err(ApiError::unauthorized(
"Missing or invalid Authorization token",
)))
}
}
#[derive(serde::Deserialize)]
struct CreateResourceRequest {
name: String,
data: String,
}
#[derive(serde::Serialize)]
struct ResourceResponse {
id: String,
name: String,
data: String,
created_by: String,
}
async fn create_resource(
user: AuthenticatedUser,
body: AutoJson<CreateResourceRequest>,
) -> actix_web::Result<HttpResponse> {
tokio::time::sleep(Duration::from_millis(100)).await;
let response = ResourceResponse {
id: uuid::Uuid::new_v4().to_string(),
name: body.name.clone(), data: body.data.clone(),
created_by: user.username,
};
Ok(HttpResponse::Created().json(response))
}
async fn health_check() -> &'static str {
"OK"
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
.init();
println!("Starting advanced microservice on http://127.0.0.1:8080");
println!("Try: curl -H 'Authorization: Bearer secret-admin-token' -H 'Content-Type: application/json' -d '{{\"name\": \"test\", \"data\": \"hello\"}}' http://127.0.0.1:8080/resource");
HttpServer::new(|| {
App::new()
.wrap(TowerLayerCompat::new(TraceLayer::new_for_http()))
.wrap(TowerLayerCompat::new(TimeoutLayer::new(Duration::from_secs(2))))
.wrap(RateLimit::new(5, Duration::from_secs(1)))
.route("/health", web::get().to(health_check))
.route("/resource", web::post().to(create_resource))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}