feat: add logic for journey->train identifier resolving, add recording of journeys, build api bearer auth

This commit is contained in:
2025-05-11 01:12:23 +02:00
parent 6b389e2d95
commit 41651dac21
25 changed files with 2833 additions and 125 deletions

View File

@ -0,0 +1,3 @@
pub async fn get() -> impl axum::response::IntoResponse {
todo!();
}

2
src/api/handlers/mod.rs Normal file
View File

@ -0,0 +1,2 @@
pub(crate) mod bingo_card;
pub(crate) mod webhook;

103
src/api/handlers/webhook.rs Normal file
View File

@ -0,0 +1,103 @@
use std::sync::Arc;
use axum::{
Json,
http::StatusCode,
extract::State,
};
use axum_core::response::IntoResponse;
use chrono::DateTime;
use serde::Deserialize;
use sqlx::PgPool;
use sqlx::postgres::PgQueryResult;
use crate::api::db_vendo_navigator::get_railcar_identifier_by_journey;
use crate::error::train_order_api_error::{CheckInError, ResolveTripNumberError};
use crate::model::app::AppState;
use crate::model::database::User;
use crate::model::travelynx::{CheckInReason, Train};
use crate::util::axum::UserBearerTokenExtractor;
#[derive(Debug, Deserialize)]
pub(crate) enum CheckIn {
Traewelling(crate::model::traewelling::CheckIn),
Travelynx(crate::model::travelynx::CheckIn),
}
#[axum::debug_handler]
pub(crate) async fn receive_travelynx(
State(app_state): State<AppState>,
UserBearerTokenExtractor(user): UserBearerTokenExtractor,
Json(body): Json<crate::model::travelynx::CheckIn>,
) -> impl IntoResponse {
receive_travelynx_checkin(body, user, app_state.db).await.into_response()
}
async fn receive_travelynx_checkin(
check_in: crate::model::travelynx::CheckIn,
user: User,
db: Arc<PgPool>
) -> impl IntoResponse {
match check_in.reason {
CheckInReason::CHECKIN => {
let railcar_identifier = get_railcar_identifier_by_journey(
check_in.status.train.train_type.unwrap(),
check_in.status.train.number.to_owned().unwrap().parse().unwrap(),
check_in.status.from_station.uic,
check_in.status.from_station.scheduled_time.unwrap()
)
.await
.map_err(|e| CheckInError::from(e))?;
let train = get_train_by_identifier(railcar_identifier as i32, db.clone())
.await
.map_err(|e| ResolveTripNumberError::from(e))
.map_err(|e| CheckInError::from(e))?;
println!("Train: {:?}", train);
record_journey(user, train, check_in.status.from_station.scheduled_time.unwrap(), db)
.await
.expect("Failed to check in!");
let message = format!(
"Successfully checked into {} {} ({}), departing from station {} at {}",
check_in.status.train.train_type.unwrap(),
check_in.status.train.number.unwrap(),
railcar_identifier,
check_in.status.from_station.uic,
check_in.status.from_station.scheduled_time.unwrap()
);
Ok::<_, CheckInError>((
StatusCode::OK,
message
).into_response())
},
CheckInReason::UNDO => {
Ok::<_, CheckInError>((
StatusCode::OK,
"Checkin undone"
).into_response())
},
_ => Ok::<_, CheckInError>((
StatusCode::OK,
"Nothing to do!"
).into_response())
}
}
async fn get_train_by_identifier(identifier: i32, db: Arc<PgPool>) -> sqlx::Result<crate::model::database::Train> {
sqlx::query_as!(
crate::model::database::Train,
"SELECT * FROM triebzug WHERE tz_id = $1",
identifier
).fetch_one(db.as_ref()).await
}
async fn record_journey(
user: User,
train: crate::model::database::Train,
timestamp: usize,
db: Arc<PgPool>
) -> sqlx::Result<PgQueryResult> {
sqlx::query!(
"INSERT INTO checkins VALUES (gen_random_uuid(), $1, $2, $3, NULL)",
user.uuid,
train.uuid,
DateTime::from_timestamp(timestamp as i64, 0).unwrap().naive_utc()
).execute(db.as_ref()).await
}