List devices API working

This commit is contained in:
rzuasti
2026-02-25 15:54:19 -05:00
parent f45a22a892
commit 9ff6c9ec7e
7 changed files with 106 additions and 8 deletions
+1
View File
@@ -1548,6 +1548,7 @@ dependencies = [
"rusqlite", "rusqlite",
"rusqlite_migration", "rusqlite_migration",
"serde", "serde",
"serde_derive",
"serde_json", "serde_json",
"tokio 1.49.0", "tokio 1.49.0",
"tower-http", "tower-http",
+1
View File
@@ -9,6 +9,7 @@ log = "0.4.29"
pnet = "0.35.0" pnet = "0.35.0"
tokio = { version = "1.0", features = ["full"] } tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_derive = "1.0"
serde_json = "1.0" serde_json = "1.0"
rusqlite = { version = "0.38.0", features = ["bundled", "chrono"] } rusqlite = { version = "0.38.0", features = ["bundled", "chrono"] }
rusqlite_migration = { version = "2.4.1", features = ["from-directory"] } rusqlite_migration = { version = "2.4.1", features = ["from-directory"] }
+1
View File
@@ -9,6 +9,7 @@ mod mac_vendor_finder;
mod model; mod model;
mod scanner; mod scanner;
mod settings; mod settings;
mod utils;
mod web_server; mod web_server;
#[cfg(test)] #[cfg(test)]
+3 -2
View File
@@ -1,4 +1,5 @@
use chrono::{DateTime, Utc, serde::ts_seconds}; use crate::utils::date_serializer;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
@@ -7,7 +8,7 @@ pub struct Device {
pub mac_address: String, pub mac_address: String,
pub ipv4_address: String, pub ipv4_address: String,
pub vendor: String, pub vendor: String,
#[serde(with = "ts_seconds")] #[serde(with = "date_serializer")]
pub last_seen: DateTime<Utc>, pub last_seen: DateTime<Utc>,
pub is_registered: bool, pub is_registered: bool,
pub owner: String, pub owner: String,
+1
View File
@@ -0,0 +1 @@
pub mod date_serializer;
+20
View File
@@ -0,0 +1,20 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error};
fn time_to_json(t: DateTime<Utc>) -> String {
t.to_rfc3339()
}
pub fn serialize<S: Serializer>(
datetime: &DateTime<Utc>,
serializer: S,
) -> Result<S::Ok, S::Error> {
time_to_json(datetime.clone()).serialize(serializer)
}
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<DateTime<Utc>, D::Error> {
let datetime: String = Deserialize::deserialize(deserializer)?;
Ok(DateTime::parse_from_str(&datetime, "%Y-%m-%d %H:%M:%S")
.map_err(D::Error::custom)?
.to_utc())
}
+79 -6
View File
@@ -1,7 +1,13 @@
use std::error::Error; use std::{collections::HashMap, error::Error};
use axum::{Json, Router, extract::Path, http::StatusCode, routing::get}; use axum::{
use log::{debug, error, info}; Json, Router,
extract::{Path, Query},
http::StatusCode,
routing::get,
};
use chrono::{DateTime, Utc};
use log::{debug, error, info, warn};
use tower_http::services::ServeDir; use tower_http::services::ServeDir;
use crate::{ use crate::{
@@ -15,7 +21,7 @@ pub async fn serve() -> Result<(), Box<dyn Error>> {
let router = Router::new() let router = Router::new()
.route("/", get(|| async { "hello" })) .route("/", get(|| async { "hello" }))
.route("/api/devices", get(get_devices)) .route("/api/devices", get(list_devices))
.route("/api/notifications", get(list_notifications)) .route("/api/notifications", get(list_notifications))
.route("/api/notifications/{id}", get(read_notification)) .route("/api/notifications/{id}", get(read_notification))
.route( .route(
@@ -33,8 +39,75 @@ pub async fn serve() -> Result<(), Box<dyn Error>> {
Ok(()) Ok(())
} }
async fn get_devices() -> Result<Json<Vec<Device>>, StatusCode> { fn parse_parameter_bool(params: &HashMap<String, String>, name: &str) -> Option<bool> {
unimplemented!(); if params.contains_key(name) {
let param_value = params.get(name).unwrap().as_str();
debug!("Found parameter {name} with value {}", param_value);
match param_value.parse::<bool>() {
Ok(value) => Some(value),
Err(_) => None,
}
} else {
None
}
}
fn parse_parameter_string(params: &HashMap<String, String>, name: &str) -> Option<String> {
if params.contains_key(name) {
let param_value = params.get(name).unwrap().as_str();
debug!("Found parameter {name} with value {}", param_value);
Some(param_value.to_string())
} else {
None
}
}
fn parse_parameter_date(params: &HashMap<String, String>, name: &str) -> Option<DateTime<Utc>> {
if params.contains_key(name) {
let mut param_value = params.get(name).unwrap().to_ascii_uppercase();
if !param_value.ends_with("Z") {
param_value.push('Z');
}
debug!("Found parameter {name} with value {}", param_value);
match DateTime::parse_from_rfc3339(param_value.as_str()) {
Ok(value) => {
debug!("Date parsed to {}", value.to_utc().to_rfc3339());
Some(value.to_utc())
}
Err(err) => {
warn!("Error parsing date from parameters: {}", err);
None
}
}
} else {
None
}
}
async fn list_devices(
Query(params): Query<HashMap<String, String>>,
) -> Result<Json<Vec<Device>>, StatusCode> {
let is_registered: Option<bool> = parse_parameter_bool(&params, "is_registered");
let last_seen_from: Option<DateTime<Utc>> = parse_parameter_date(&params, "last_seen_from");
let last_seen_to: Option<DateTime<Utc>> = parse_parameter_date(&params, "last_seen_to");
let owner: Option<String> = parse_parameter_string(&params, "owner");
let device_type: Option<String> = parse_parameter_string(&params, "device_type");
let vendor: Option<String> = parse_parameter_string(&params, "vendor");
match db::devices::list_devices(
is_registered,
last_seen_from,
last_seen_to,
owner,
device_type,
vendor,
) {
Ok(value) => Ok(Json(value)),
Err(err) => {
error!("Error listing devices: {}", err);
Err(StatusCode::INTERNAL_SERVER_ERROR)
}
}
} }
async fn read_notification(Path(id): Path<i64>) -> Result<Json<Notification>, StatusCode> { async fn read_notification(Path(id): Path<i64>) -> Result<Json<Notification>, StatusCode> {