this repo has no description

add basic day-wise route-unlocking middleware

Signed-off-by: oppiliappan <me@oppi.li>

oppi.li efde74d5 85254751

verified
Changed files
+62 -4
src
+1
Cargo.lock
··· 22 version = "0.1.0" 23 dependencies = [ 24 "axum", 25 "tokio", 26 ] 27
··· 22 version = "0.1.0" 23 dependencies = [ 24 "axum", 25 + "serde_json", 26 "tokio", 27 ] 28
+1
Cargo.toml
··· 5 6 [dependencies] 7 axum = "0.8.4" 8 tokio = { version = "1.46.1", features = ["full"] }
··· 5 6 [dependencies] 7 axum = "0.8.4" 8 + serde_json = "1.0.141" 9 tokio = { version = "1.46.1", features = ["full"] }
+20 -4
src/main.rs
··· 1 - use axum::{Router, routing::get}; 2 use std::net::{IpAddr, Ipv4Addr, SocketAddr}; 3 4 #[tokio::main] 5 async fn main() { 6 - let app = Router::new().route("/", get(handler)); 7 let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 7878); 8 let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); 9 println!("listening on {}", addr); 10 axum::serve(listener, app).await.unwrap(); 11 } 12 13 - async fn handler() -> &'static str { 14 - "hello, world!" 15 }
··· 1 + use axum::extract::Path; 2 + use axum::{Router, middleware, routing::get}; 3 use std::net::{IpAddr, Ipv4Addr, SocketAddr}; 4 + use std::time; 5 + 6 + mod unlock; 7 8 #[tokio::main] 9 async fn main() { 10 let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 7878); 11 let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); 12 println!("listening on {}", addr); 13 + 14 + let now = time::Instant::now(); 15 + let daily = time::Duration::from_secs(24 * 60 * 60); 16 + let state = unlock::Unlock::new(now, daily); 17 + 18 + let app = 19 + Router::new() 20 + .route("/day/{id}", get(handler)) 21 + .route_layer(middleware::from_fn_with_state( 22 + state.clone(), 23 + unlock::unlock, 24 + )); 25 + 26 axum::serve(listener, app).await.unwrap(); 27 } 28 29 + async fn handler(Path(id): Path<u32>) -> String { 30 + format!("hello day {id}") 31 }
+40
src/unlock.rs
···
··· 1 + use axum::extract::{Path, Request, State}; 2 + use axum::http; 3 + use axum::{ 4 + middleware, 5 + response::{self, IntoResponse}, 6 + }; 7 + use std::time; 8 + 9 + #[derive(Clone)] 10 + pub struct Unlock { 11 + start: time::Instant, 12 + interval: time::Duration, 13 + } 14 + 15 + impl Unlock { 16 + pub fn new(start: time::Instant, interval: time::Duration) -> Self { 17 + Self { start, interval } 18 + } 19 + } 20 + 21 + pub async fn unlock( 22 + Path(day): Path<u32>, 23 + State(unlocker): State<Unlock>, 24 + request: Request, 25 + next: middleware::Next, 26 + ) -> response::Response { 27 + let deadline = unlocker.start + unlocker.interval * day; 28 + let now = time::Instant::now(); 29 + if now >= deadline { 30 + return next.run(request).await; 31 + } 32 + 33 + let time_remaining = deadline.saturating_duration_since(now); 34 + let error_response = axum::Json(serde_json::json!({ 35 + "error": "Route Locked", 36 + "time_remaining_seconds": time_remaining.as_secs(), 37 + })); 38 + 39 + (http::StatusCode::FORBIDDEN, error_response).into_response() 40 + }