use axum::{ extract::Query, http::StatusCode, response::IntoResponse, routing::{get, post}, Form, Json, Router, }; use serde::{Deserialize, Serialize}; use tracing::{event, instrument, Level}; use crate::auth::AuthenticationError; use crate::secrets::Password; #[instrument] pub async fn run() -> color_eyre::Result<()> { let app = Router::new() .route("/tls-check/success", get(|| async { "OK" })) .route("/api-login", post(api_login)); let listener = tokio::net::TcpListener::bind("[::]:8080").await?; axum::serve(listener, app).await?; Ok(()) } #[derive(Debug, Clone, PartialEq, Eq, Serialize)] struct ApiError { #[serde(skip_serializing)] status: StatusCode, error: String, message: String, } impl From for ApiError { fn from(err: AuthenticationError) -> Self { Self { status: StatusCode::UNAUTHORIZED, error: "authentication-failed".to_owned(), message: err.to_string(), } } } impl IntoResponse for ApiError { fn into_response(self) -> axum::response::Response { (self.status, Json(self)).into_response() } } type ApiResult = Result; #[derive(Debug, Clone, PartialEq, Eq, Deserialize)] struct ApiVersion { api_version: String, } #[derive(Debug, Clone, Deserialize)] struct LoginRequest { username: String, password: Password, } #[derive(Debug, Clone, Serialize)] struct LoginResponse { username: String, token: String, } #[instrument] async fn api_login( Query(ApiVersion { api_version }): Query, Form(LoginRequest { username, password }): Form, ) -> ApiResult> { event!(Level::WARN, "Creating dummy token"); Ok(Json(LoginResponse { username, token: "invalid".to_string(), })) }