feat: reorganize with remote
This commit is contained in:
parent
a74e5753fa
commit
dc7b8cbadd
28 changed files with 622 additions and 3024 deletions
|
|
@ -1,13 +1,14 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use axum::extract::{Query, State};
|
||||
use axum::extract::State;
|
||||
use axum::http::{HeaderMap, StatusCode};
|
||||
use axum::Json;
|
||||
use noisebell_common::{validate_bearer, HistoryEntry, WebhookPayload};
|
||||
use tokio::sync::Mutex;
|
||||
use tracing::{error, info};
|
||||
|
||||
use crate::db;
|
||||
use crate::types::{DoorStatus, InboundWebhook, OutboundPayload, WebhookTarget};
|
||||
use crate::types::{DoorStatus, WebhookTarget};
|
||||
use crate::webhook;
|
||||
|
||||
pub struct AppState {
|
||||
|
|
@ -26,18 +27,10 @@ fn unix_now() -> u64 {
|
|||
.as_secs()
|
||||
}
|
||||
|
||||
fn validate_bearer(headers: &HeaderMap, expected: &str) -> bool {
|
||||
headers
|
||||
.get("authorization")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.map(|v| v.strip_prefix("Bearer ").unwrap_or("") == expected)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub async fn post_webhook(
|
||||
State(state): State<Arc<AppState>>,
|
||||
headers: HeaderMap,
|
||||
Json(body): Json<InboundWebhook>,
|
||||
Json(body): Json<WebhookPayload>,
|
||||
) -> StatusCode {
|
||||
if !validate_bearer(&headers, &state.inbound_api_key) {
|
||||
return StatusCode::UNAUTHORIZED;
|
||||
|
|
@ -48,12 +41,18 @@ pub async fn post_webhook(
|
|||
};
|
||||
|
||||
let now = unix_now();
|
||||
{
|
||||
let conn = state.db.lock().await;
|
||||
if let Err(e) = db::update_state(&conn, status, body.timestamp, now) {
|
||||
error!(error = %e, "failed to update state from webhook");
|
||||
return StatusCode::INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
let db = state.db.clone();
|
||||
let timestamp = body.timestamp;
|
||||
let result = tokio::task::spawn_blocking(move || {
|
||||
let conn = db.blocking_lock();
|
||||
db::update_state(&conn, status, timestamp, now)
|
||||
})
|
||||
.await
|
||||
.expect("db task panicked");
|
||||
|
||||
if let Err(e) = result {
|
||||
error!(error = %e, "failed to update state from webhook");
|
||||
return StatusCode::INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
info!(status = status.as_str(), timestamp = body.timestamp, "state updated via webhook");
|
||||
|
|
@ -61,7 +60,7 @@ pub async fn post_webhook(
|
|||
webhook::forward(
|
||||
&state.client,
|
||||
&state.webhooks,
|
||||
&OutboundPayload {
|
||||
&WebhookPayload {
|
||||
status: status.as_str().to_string(),
|
||||
timestamp: body.timestamp,
|
||||
},
|
||||
|
|
@ -74,43 +73,56 @@ pub async fn post_webhook(
|
|||
}
|
||||
|
||||
pub async fn get_status(State(state): State<Arc<AppState>>) -> Result<Json<serde_json::Value>, StatusCode> {
|
||||
let conn = state.db.lock().await;
|
||||
match db::get_status(&conn) {
|
||||
Ok(status) => Ok(Json(serde_json::to_value(status).unwrap())),
|
||||
Err(e) => {
|
||||
error!(error = %e, "failed to get status");
|
||||
Err(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
}
|
||||
let db = state.db.clone();
|
||||
let status = tokio::task::spawn_blocking(move || {
|
||||
let conn = db.blocking_lock();
|
||||
db::get_status(&conn)
|
||||
})
|
||||
.await
|
||||
.expect("db task panicked")
|
||||
.map_err(|e| {
|
||||
error!(error = %e, "failed to get status");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
Ok(Json(serde_json::to_value(status).unwrap()))
|
||||
}
|
||||
|
||||
pub async fn get_info(State(state): State<Arc<AppState>>) -> Result<Json<serde_json::Value>, StatusCode> {
|
||||
let conn = state.db.lock().await;
|
||||
match db::get_pi_info(&conn) {
|
||||
Ok(info) => Ok(Json(info)),
|
||||
Err(e) => {
|
||||
error!(error = %e, "failed to get pi info");
|
||||
Err(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
}
|
||||
let db = state.db.clone();
|
||||
let info = tokio::task::spawn_blocking(move || {
|
||||
let conn = db.blocking_lock();
|
||||
db::get_pi_info(&conn)
|
||||
})
|
||||
.await
|
||||
.expect("db task panicked")
|
||||
.map_err(|e| {
|
||||
error!(error = %e, "failed to get pi info");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
Ok(Json(info))
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
pub struct HistoryQuery {
|
||||
pub limit: Option<u32>,
|
||||
pub async fn health() -> StatusCode {
|
||||
StatusCode::OK
|
||||
}
|
||||
|
||||
pub async fn get_history(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Query(query): Query<HistoryQuery>,
|
||||
) -> Result<Json<Vec<crate::types::HistoryEntry>>, StatusCode> {
|
||||
let limit = query.limit.unwrap_or(50);
|
||||
let conn = state.db.lock().await;
|
||||
match db::get_history(&conn, limit) {
|
||||
Ok(entries) => Ok(Json(entries)),
|
||||
Err(e) => {
|
||||
error!(error = %e, "failed to get history");
|
||||
Err(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
}
|
||||
) -> Result<Json<Vec<HistoryEntry>>, StatusCode> {
|
||||
let limit = 100u32;
|
||||
let db = state.db.clone();
|
||||
let entries = tokio::task::spawn_blocking(move || {
|
||||
let conn = db.blocking_lock();
|
||||
db::get_history(&conn, limit)
|
||||
})
|
||||
.await
|
||||
.expect("db task panicked")
|
||||
.map_err(|e| {
|
||||
error!(error = %e, "failed to get history");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
Ok(Json(entries))
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue