This commit is contained in:
@@ -5,7 +5,7 @@ meta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
url: http://localhost:8484/device
|
url: http://localhost:8282/device
|
||||||
body: none
|
body: none
|
||||||
auth: none
|
auth: none
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ meta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
put {
|
put {
|
||||||
url: http://localhost:8282/firmware/waterlevel/INA233/1.0.0
|
url: http://localhost:8282/firmware/waterlevel/INA226/1.0.3
|
||||||
body: multipartForm
|
body: multipartForm
|
||||||
auth: none
|
auth: none
|
||||||
}
|
}
|
||||||
|
|||||||
56
src/main.rs
56
src/main.rs
@@ -7,7 +7,7 @@ use dotenvy::dotenv;
|
|||||||
use log::{debug, error, info};
|
use log::{debug, error, info};
|
||||||
use schemas::{BoardConfig, BoardType, Device, OTAConfiguration, OTAConfigurationList};
|
use schemas::{BoardConfig, BoardType, Device, OTAConfiguration, OTAConfigurationList};
|
||||||
use sqlx::types::mac_address::MacAddress;
|
use sqlx::types::mac_address::MacAddress;
|
||||||
use util::parse_mac_address;
|
use util::{get_files, parse_mac_address};
|
||||||
|
|
||||||
mod database;
|
mod database;
|
||||||
mod schemas;
|
mod schemas;
|
||||||
@@ -15,6 +15,8 @@ mod util;
|
|||||||
|
|
||||||
struct AppState {
|
struct AppState {
|
||||||
db: Database,
|
db: Database,
|
||||||
|
firmwares_path: PathBuf,
|
||||||
|
hostname: String
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/telemetry/{device_id}")]
|
#[post("/telemetry/{device_id}")]
|
||||||
@@ -124,16 +126,17 @@ async fn get_devices(data: web::Data<AppState>) -> impl Responder {
|
|||||||
HttpResponse::Ok().json(devices)
|
HttpResponse::Ok().json(devices)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[put("/firmware/{product}/{config}/{version}")]
|
#[put("/firmware/{device}/{config}/{version}")]
|
||||||
async fn upload_firmware(path: web::Path<(String, String, String)>, body: web::Bytes) -> impl Responder {
|
async fn upload_firmware(data: web::Data<AppState>, path: web::Path<(String, String, String)>, body: web::Bytes) -> impl Responder {
|
||||||
let (product, config, version) = path.into_inner();
|
let (device, config, version) = path.into_inner();
|
||||||
let version = version.replace(".", "_");
|
let version = version.replace(".", "-");
|
||||||
let firmware_root_path = PathBuf::from(env::var("FIMRWARE_FOLDER").unwrap_or("./firmware".to_string()));
|
|
||||||
|
|
||||||
let firmware_folder = firmware_root_path.join(&product).join(&config);
|
let firmware_root_path = &data.firmwares_path;
|
||||||
let firmware_path = firmware_folder.join(format!("ver_{}", &version)).with_extension("bin");
|
|
||||||
|
|
||||||
info!("Uploading firmware with product: {product}, config: {config} and version: {version} to {firmware_path:?}");
|
let firmware_folder = firmware_root_path.join(&device);
|
||||||
|
let firmware_path = firmware_folder.join(format!("firmware_{config}_{version}")).with_extension("bin");
|
||||||
|
|
||||||
|
info!("Uploading firmware with product: {device}, config: {config} and version: {version} to {firmware_path:?}");
|
||||||
|
|
||||||
fs::create_dir_all(&firmware_folder).unwrap();
|
fs::create_dir_all(&firmware_folder).unwrap();
|
||||||
let x = tokio::fs::write(&firmware_path, &body).await;
|
let x = tokio::fs::write(&firmware_path, &body).await;
|
||||||
@@ -142,18 +145,32 @@ async fn upload_firmware(path: web::Path<(String, String, String)>, body: web::B
|
|||||||
HttpResponse::Ok().body(format!("Firmware version {} uploaded successfully", version))
|
HttpResponse::Ok().body(format!("Firmware version {} uploaded successfully", version))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/firmware/{product}/{config}/{version}")]
|
#[get("/firmware/{device}")]
|
||||||
async fn get_firmware_json(path: web::Path<(String, String, String)>) -> impl Responder {
|
async fn get_firmware_json(data: web::Data<AppState>, path: web::Path<String>) -> impl Responder {
|
||||||
let (product, config, version) = path.into_inner();
|
let device = path.into_inner();
|
||||||
let version = version.replace(".", "_");
|
let fw_path = &data.firmwares_path.join(device);
|
||||||
|
if fw_path.is_dir() {
|
||||||
|
match get_files(fw_path, &data.hostname) {
|
||||||
|
Ok(cfg) => HttpResponse::Ok().json(OTAConfigurationList{configurations: cfg} ),
|
||||||
|
Err(e) => HttpResponse::InternalServerError().body(e.to_string())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
HttpResponse::Ok().json(OTAConfigurationList{configurations: vec![]} )
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
let mut configs = Vec::new();
|
|
||||||
|
|
||||||
|
|
||||||
HttpResponse::Ok().json(OTAConfigurationList{configurations: configs} )
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #[get("/firmware/{product}/{config}/{version}")]
|
||||||
|
// async fn get_firmware_json(path: web::Path<(String, String, String)>, data: web::Data<AppState>) -> impl Responder {
|
||||||
|
// let (product, config, version) = path.into_inner();
|
||||||
|
// let version = version.replace(".", "-");
|
||||||
|
|
||||||
|
// match get_files(&data.firmwares_path, &data.hostname) {
|
||||||
|
// Ok(cfg) => HttpResponse::Ok().json(OTAConfigurationList{configurations: cfg} ),
|
||||||
|
// Err(e) => HttpResponse::InternalServerError().body(e.to_string())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
#[get("/firmware/waterlevel/firmware_INA233_1.0.0.bin")]
|
#[get("/firmware/waterlevel/firmware_INA233_1.0.0.bin")]
|
||||||
async fn serve_firmware() -> impl Responder {
|
async fn serve_firmware() -> impl Responder {
|
||||||
let file_path = PathBuf::from("./firmware/waterlevel/firmware_INA233_1.0.0.bin");
|
let file_path = PathBuf::from("./firmware/waterlevel/firmware_INA233_1.0.0.bin");
|
||||||
@@ -178,6 +195,7 @@ async fn main() -> std::io::Result<()> {
|
|||||||
env_logger::init();
|
env_logger::init();
|
||||||
info!("Starting");
|
info!("Starting");
|
||||||
|
|
||||||
|
|
||||||
let db_url = match env::var("DATABASE_URL") {
|
let db_url = match env::var("DATABASE_URL") {
|
||||||
Ok(url) => url,
|
Ok(url) => url,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -192,7 +210,7 @@ async fn main() -> std::io::Result<()> {
|
|||||||
|
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
App::new()
|
App::new()
|
||||||
.app_data(web::Data::new(AppState { db: db.clone() }))
|
.app_data(web::Data::new(AppState { db: db.clone(), firmwares_path: PathBuf::from("./fw"), hostname: "127.0.0.1".to_string() }))
|
||||||
.app_data(web::PayloadConfig::new(256 * 1024 * 1024)) //256MB
|
.app_data(web::PayloadConfig::new(256 * 1024 * 1024)) //256MB
|
||||||
.service(receive_telemetry)
|
.service(receive_telemetry)
|
||||||
.service(get_telemetry)
|
.service(get_telemetry)
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use serde::{ser::SerializeStruct, Deserialize, Serialize};
|
use serde::{ser::SerializeStruct, Deserialize, Serialize};
|
||||||
use sqlx::types::mac_address::MacAddress;
|
use sqlx::types::mac_address::MacAddress;
|
||||||
|
|||||||
@@ -52,12 +52,13 @@ pub fn get_files(root_path: &PathBuf, hostname: &str) -> Result<Vec<OTAConfigura
|
|||||||
let entries = fs::read_dir(root_path)?;
|
let entries = fs::read_dir(root_path)?;
|
||||||
|
|
||||||
for entry in entries.flatten() {
|
for entry in entries.flatten() {
|
||||||
|
info!("Reading entry: {entry:?}");
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
if path.is_file() {
|
if path.is_file() {
|
||||||
println!("File: {:?}", path);
|
info!("processing file: {:?}", path);
|
||||||
// Splits the filename at the underscores. This is safe to do as names get sanitized on upload and are only uploaded by the pipeline
|
// Splits the filename at the underscores. This is safe to do as names get sanitized on upload and are only uploaded by the pipeline
|
||||||
let split_name: Vec<_> = path.file_name().ok_or(GetFilesError::Filename)?.to_str().ok_or(GetFilesError::Filename)?.split("_").collect();
|
let split_name: Vec<_> = path.file_name().ok_or(GetFilesError::Filename)?.to_str().ok_or(GetFilesError::Filename)?.split("_").collect();
|
||||||
let version = split_name[2].strip_suffix(".bin").ok_or(GetFilesError::Extension)?;
|
let version = split_name[2].strip_suffix(".bin").ok_or(GetFilesError::Extension)?.replace("-", ".");
|
||||||
let board_config = BoardConfig::from_str(split_name[1])?;
|
let board_config = BoardConfig::from_str(split_name[1])?;
|
||||||
let board_type = BoardType::from_str(&product_name).unwrap();
|
let board_type = BoardType::from_str(&product_name).unwrap();
|
||||||
let version_replaced = version.replace(".", "_");
|
let version_replaced = version.replace(".", "_");
|
||||||
@@ -85,8 +86,8 @@ mod tests {
|
|||||||
let expected_2 = OTAConfiguration{ version: "4.5.6".to_string(), url: "example.com/firmware/waterlevel/INA226/4_5_6.bin".to_string(), board: Some(BoardType::Waterlevel), config: Some(BoardConfig::INA226) };
|
let expected_2 = OTAConfiguration{ version: "4.5.6".to_string(), url: "example.com/firmware/waterlevel/INA226/4_5_6.bin".to_string(), board: Some(BoardType::Waterlevel), config: Some(BoardConfig::INA226) };
|
||||||
let loaded_configs = get_files(&PathBuf::from("./test/waterlevel"), "example.com").unwrap();
|
let loaded_configs = get_files(&PathBuf::from("./test/waterlevel"), "example.com").unwrap();
|
||||||
|
|
||||||
assert_eq!(loaded_configs[0], expected_1);
|
assert_eq!(loaded_configs[1], expected_1);
|
||||||
assert_eq!(loaded_configs[1], expected_2);
|
assert_eq!(loaded_configs[0], expected_2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user