changed backedn framwork from axum to actix and also added openapi and swagger ui

This commit is contained in:
2026-02-04 22:55:19 +01:00
parent 9b093e6739
commit e135a34d4c
11 changed files with 1716 additions and 91 deletions

1641
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,3 +10,7 @@ chrono = { version = "0.4.42", features = ["serde"] }
axum = { version = "0.8.8", features = ["macros"] }
tokio = { version = "1.49.0", features = ["full"] }
serde = { version = "1.0.228", features = ["derive"] }
actix-web = "4.12.1"
paperclip = { version = "0.9.5", features = ["actix4"] }
apistos = { version = "0.6.0", features = ["swagger-ui"] }
schemars = { package = "apistos-schemars", version = "0.8" }

View File

@@ -1,35 +0,0 @@
use axum::{Json, extract::State, http::StatusCode};
use chrono::{DateTime, Local};
use serde::{Deserialize, Serialize};
use crate::{AppState};
use crate::types::Alarm;
#[derive(Deserialize, Serialize)]
pub struct CreateAlarmRequest {
time: DateTime<Local>
}
#[derive(Deserialize, Serialize)]
pub struct CreateAlarmResponse {
alarm: Alarm
}
#[derive(Deserialize, Serialize)]
pub struct AlarmError {
message: String
}
#[axum::debug_handler]
pub async fn create_alarm(
State(state): State<AppState>,
Json(create_request): Json<CreateAlarmRequest>
) -> Result<(StatusCode, Json<CreateAlarmResponse>), (StatusCode, Json<AlarmError>)> {
let result = state.scheduler.add_alarm(create_request.time);
match result {
Ok(alarm) => Ok((StatusCode::CREATED, Json(CreateAlarmResponse { alarm }))),
Err(e) => Err((StatusCode::INTERNAL_SERVER_ERROR, Json(AlarmError { message: e }))),
}
}

View File

@@ -1 +0,0 @@
pub mod alarm;

View File

@@ -1,17 +1,16 @@
use std::{
sync::{Arc, Mutex},
};
use std::sync::{Arc, Mutex};
use actix_web::{App, HttpServer, web};
use apistos::{SwaggerUIConfig, app::{BuildConfig, OpenApiWrapper}, info::Info, spec::Spec};
use cron_tab::Cron;
use gpio_cdev::Chip;
use crate::{ringer::BeepRinger, scheduler::Scheduler};
mod handler;
mod router;
mod scheduler;
mod ringer;
mod types;
mod resources;
#[derive(Clone)]
@@ -36,16 +35,30 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
app_state.scheduler.start();
start_server(app_state).await;
let _ = start_actix_server(app_state).await;
Ok(())
}
async fn start_server(app_state: AppState) {
let router = router::router(app_state);
let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await.unwrap();
async fn start_actix_server(app_state: AppState) -> std::io::Result<()> {
let _ = HttpServer::new(move || {
let spec = Spec {
info: Info {
title: "Snooze Pal".to_string(),
version: "0.0.1".to_string(),
..Default::default()
},
..Default::default()
};
println!("Listening on http://0.0.0.0:8080");
axum::serve(listener, router).await.unwrap();
App::new()
.document(spec)
.service(resources::v1())
.app_data(web::Data::new(app_state.clone()))
.build_with("/openapi.json", BuildConfig::default().with(SwaggerUIConfig::new(&"/swagger")))
}).bind("0.0.0.0:8080")?.run().await;
Ok(())
}

View File

@@ -0,0 +1,7 @@
mod post;
use apistos::web;
pub fn resource() -> web::Resource {
web::resource("/alarm")
.route(web::post().to(post::post))
}

View File

@@ -0,0 +1,56 @@
use std::{fmt::Display};
use actix_web::{ResponseError, web::{Data, Json}};
use apistos::{ApiComponent, ApiErrorComponent, actix::CreatedJson};
use chrono::{DateTime, Local};
use schemars::JsonSchema;
use serde::{Serialize, Deserialize};
use crate::AppState;
#[derive(Debug, Deserialize, JsonSchema, ApiComponent)]
pub struct RequestBody {
time: DateTime<Local>,
}
#[derive(Debug, Serialize, JsonSchema, ApiComponent)]
pub struct SuccessResponse {
pub time: DateTime<Local>,
pub enabled: bool
}
#[derive(Debug, Deserialize, Serialize, ApiErrorComponent)]
#[openapi_error(status(code = 500, description = "The alarm could not be created"))]
pub enum ErrorResponse {
InternalError(String)
}
impl Display for ErrorResponse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ErrorResponse::InternalError(e) => write!(f, "Internal error: {}", e),
}
}
}
impl ResponseError for ErrorResponse {
fn status_code(&self) -> actix_web::http::StatusCode {
match self {
ErrorResponse::InternalError(_) => actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
}
}
}
#[apistos::api_operation(
summary = "Add new alarm",
description = r###"Creates new Alarm"###,
error_code= 500,
)]
pub async fn post(data: Data<AppState>, body: Json<RequestBody>) -> Result<CreatedJson<SuccessResponse>, ErrorResponse> {
let result = data.scheduler.add_alarm(body.time);
match result {
Ok(alarm) => Ok(CreatedJson(SuccessResponse { time: alarm.time, enabled: alarm.enabled })),
Err(e) => Err(ErrorResponse::InternalError(e)),
}
}

5
src/resources/mod.rs Normal file
View File

@@ -0,0 +1,5 @@
mod alarm;
pub fn v1() -> apistos::web::Scope {
apistos::web::scope("/v1").service(alarm::resource())
}

View File

@@ -1,6 +0,0 @@
use crate::{handler::alarm::create_alarm, AppState};
use axum::{routing::post, Router};
pub fn router() -> Router<AppState> {
Router::new().route("/", post(create_alarm))
}

View File

@@ -1,9 +0,0 @@
use axum::Router;
mod alarm;
pub fn router(app_state: crate::AppState) -> Router {
Router::new()
.nest("/alarm", alarm::router())
.with_state(app_state)
}

View File

@@ -1,10 +1,12 @@
use apistos::ApiComponent;
use chrono::{DateTime, Local};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Clone)]
#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema, ApiComponent)]
pub struct Alarm {
enabled: bool,
time: DateTime<Local>,
pub enabled: bool,
pub time: DateTime<Local>,
}
impl Alarm {