This commit is contained in:
@@ -12,6 +12,7 @@ chrono = { version = "0.4.31", features = ["serde"] }
|
|||||||
dotenvy = "0.15"
|
dotenvy = "0.15"
|
||||||
env_logger = "0.11"
|
env_logger = "0.11"
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
|
semver = "1.0.25"
|
||||||
serde = "1.0.188"
|
serde = "1.0.188"
|
||||||
sqlx = { version = "0.8", features = [ "runtime-tokio", "tls-rustls", "postgres", "migrate", "chrono", "mac_address"] }
|
sqlx = { version = "0.8", features = [ "runtime-tokio", "tls-rustls", "postgres", "migrate", "chrono", "mac_address"] }
|
||||||
sqlx-cli = "0.8"
|
sqlx-cli = "0.8"
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ use std::{fs, path::PathBuf};
|
|||||||
|
|
||||||
use actix_web::{get, put, web, HttpResponse, Responder};
|
use actix_web::{get, put, web, HttpResponse, Responder};
|
||||||
use log::{debug, info, warn};
|
use log::{debug, info, warn};
|
||||||
|
use semver::Version;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
schemas::{AppState, OTAConfigurationList, Services},
|
schemas::{AppState, OTAConfigurationList, Services},
|
||||||
util::get_files,
|
util::{get_files, prune_files},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Upload Firmware file
|
// Upload Firmware file
|
||||||
@@ -17,7 +18,11 @@ async fn upload_firmware(
|
|||||||
body: web::Bytes,
|
body: web::Bytes,
|
||||||
) -> impl Responder {
|
) -> impl Responder {
|
||||||
let (service, device, config, version) = path.into_inner();
|
let (service, device, config, version) = path.into_inner();
|
||||||
let version = version.replace('.', "-");
|
let Ok(version) = Version::parse(&version) else {
|
||||||
|
return HttpResponse::InternalServerError().body("Failed to parse version");
|
||||||
|
};
|
||||||
|
debug!("{version:?}");
|
||||||
|
let version = format!("{}-{}-{}", version.major, version.minor, version.patch);
|
||||||
|
|
||||||
let firmware_root_path = &data.firmwares_path;
|
let firmware_root_path = &data.firmwares_path;
|
||||||
let Ok(service) = Services::from_str(&service) else {
|
let Ok(service) = Services::from_str(&service) else {
|
||||||
@@ -43,7 +48,11 @@ async fn upload_firmware(
|
|||||||
let x = tokio::fs::write(&firmware_path, &body).await;
|
let x = tokio::fs::write(&firmware_path, &body).await;
|
||||||
debug!("{x:?}");
|
debug!("{x:?}");
|
||||||
|
|
||||||
|
debug!("pruning now");
|
||||||
|
prune_files(firmware_folder, service, 3);
|
||||||
|
|
||||||
HttpResponse::Ok().body(format!("Firmware version {version} uploaded successfully"))
|
HttpResponse::Ok().body(format!("Firmware version {version} uploaded successfully"))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/{service}/{device}")]
|
#[get("/{service}/{device}")]
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
|
use semver::Version;
|
||||||
use serde::{ser::SerializeStruct, Deserialize, Serialize};
|
use serde::{ser::SerializeStruct, Deserialize, Serialize};
|
||||||
use sqlx::types::mac_address::MacAddress;
|
use sqlx::types::mac_address::MacAddress;
|
||||||
use strum::{Display, EnumString};
|
use strum::{Display, EnumString};
|
||||||
@@ -76,7 +77,7 @@ pub struct OTAConfigurationList {
|
|||||||
#[derive(serde::Serialize, PartialEq, Debug)]
|
#[derive(serde::Serialize, PartialEq, Debug)]
|
||||||
#[serde(rename_all = "PascalCase")]
|
#[serde(rename_all = "PascalCase")]
|
||||||
pub struct OTAConfiguration {
|
pub struct OTAConfiguration {
|
||||||
pub version: String,
|
pub version: Version,
|
||||||
#[serde(rename = "URL")]
|
#[serde(rename = "URL")]
|
||||||
pub url: String,
|
pub url: String,
|
||||||
pub board: Option<BoardType>,
|
pub board: Option<BoardType>,
|
||||||
|
|||||||
37
src/util.rs
37
src/util.rs
@@ -1,10 +1,11 @@
|
|||||||
use std::{fs, path::PathBuf};
|
use std::{cmp::Reverse, fs, path::PathBuf};
|
||||||
|
|
||||||
use log::{debug, info};
|
use log::{error, info};
|
||||||
|
use semver::Version;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::schemas::{BoardConfig, BoardType, OTAConfiguration};
|
use crate::schemas::{BoardConfig, BoardType, OTAConfiguration, Services};
|
||||||
|
|
||||||
pub fn parse_mac_address(mac: &str) -> Result<[u8; 6], MacAddressError> {
|
pub fn parse_mac_address(mac: &str) -> Result<[u8; 6], MacAddressError> {
|
||||||
if mac.len() != 12 {
|
if mac.len() != 12 {
|
||||||
@@ -38,6 +39,8 @@ pub enum GetFilesError {
|
|||||||
Extension,
|
Extension,
|
||||||
#[error("Strum parse Error")]
|
#[error("Strum parse Error")]
|
||||||
Conversion(#[from] strum::ParseError),
|
Conversion(#[from] strum::ParseError),
|
||||||
|
#[error("Failed to parse the version")]
|
||||||
|
VersionParse(#[from] semver::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_files(
|
pub fn get_files(
|
||||||
@@ -73,11 +76,12 @@ pub fn get_files(
|
|||||||
let board_config = BoardConfig::from_str(split_name[1])?;
|
let board_config = BoardConfig::from_str(split_name[1])?;
|
||||||
let service = split_name[0];
|
let service = split_name[0];
|
||||||
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 = Version::parse(&version)?;
|
||||||
|
let version_replaced = format!("{}-{}-{}", version.major, version.minor, version.patch);
|
||||||
let fw_url =
|
let fw_url =
|
||||||
format!("{hostname}/{service}/{board_type}/{board_config}/{version_replaced}.bin");
|
format!("{hostname}/{service}/{board_type}/{board_config}/{version_replaced}.bin");
|
||||||
let cfg = OTAConfiguration {
|
let cfg = OTAConfiguration {
|
||||||
version: version.to_string(),
|
version: version,
|
||||||
url: fw_url,
|
url: fw_url,
|
||||||
board: Some(board_type),
|
board: Some(board_type),
|
||||||
config: Some(board_config),
|
config: Some(board_config),
|
||||||
@@ -91,6 +95,25 @@ pub fn get_files(
|
|||||||
Ok(configs)
|
Ok(configs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn prune_files(path: PathBuf, service: Services, keep_last: usize) {
|
||||||
|
let Ok(mut config_list) = get_files(&path, "irrelevant") else {
|
||||||
|
error!("failed to get file list for pruning");
|
||||||
|
return
|
||||||
|
};
|
||||||
|
config_list.sort_by_key(|x| x.version.clone());
|
||||||
|
config_list.truncate(config_list.len() - keep_last);
|
||||||
|
for cfg in config_list {
|
||||||
|
println!("{cfg:?}");
|
||||||
|
let path = path.to_string_lossy();
|
||||||
|
let board_type = cfg. config.unwrap();
|
||||||
|
let path_to_remove = format!("{path}/{service}_{}_{}-{}-{}.bin", board_type, cfg.version.major, cfg.version.minor, cfg.version.patch);
|
||||||
|
if let Err(e) = fs::remove_file(&path_to_remove) {
|
||||||
|
error!("Failed to delete {path_to_remove}, {e:?}");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -98,13 +121,13 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_file_loading() {
|
fn test_file_loading() {
|
||||||
let expected_1 = OTAConfiguration {
|
let expected_1 = OTAConfiguration {
|
||||||
version: "1.2.3".to_string(),
|
version: Version::parse("1.2.3").unwrap(),
|
||||||
url: "example.com/firmware/waterlevel/INA233/1_2_3.bin".to_string(),
|
url: "example.com/firmware/waterlevel/INA233/1_2_3.bin".to_string(),
|
||||||
board: Some(BoardType::Waterlevel),
|
board: Some(BoardType::Waterlevel),
|
||||||
config: Some(BoardConfig::INA233),
|
config: Some(BoardConfig::INA233),
|
||||||
};
|
};
|
||||||
let expected_2 = OTAConfiguration {
|
let expected_2 = OTAConfiguration {
|
||||||
version: "4.5.6".to_string(),
|
version: Version::parse("4.5.6").unwrap(),
|
||||||
url: "example.com/firmware/waterlevel/INA226/4_5_6.bin".to_string(),
|
url: "example.com/firmware/waterlevel/INA226/4_5_6.bin".to_string(),
|
||||||
board: Some(BoardType::Waterlevel),
|
board: Some(BoardType::Waterlevel),
|
||||||
config: Some(BoardConfig::INA226),
|
config: Some(BoardConfig::INA226),
|
||||||
|
|||||||
Reference in New Issue
Block a user