All checks were successful
continuous-integration/drone/push Build is passing
149 lines
4.3 KiB
Rust
149 lines
4.3 KiB
Rust
use crate::constants;
|
|
use crate::devices::device::DeviceId;
|
|
use crate::energy::energy_actor;
|
|
use crate::ota::ota_manager;
|
|
use crate::ota::ota_update::OTAPlatform;
|
|
use crate::server::WebEnergyActor;
|
|
use crate::server::custom_error::HttpResult;
|
|
use actix_multipart::form::MultipartForm;
|
|
use actix_multipart::form::tempfile::TempFile;
|
|
use actix_web::{HttpResponse, web};
|
|
|
|
pub async fn supported_platforms() -> HttpResult {
|
|
Ok(HttpResponse::Ok().json(OTAPlatform::supported_platforms()))
|
|
}
|
|
|
|
#[derive(Debug, MultipartForm)]
|
|
pub struct UploadForm {
|
|
#[multipart(rename = "firmware")]
|
|
firmware: Vec<TempFile>,
|
|
}
|
|
|
|
#[derive(serde::Deserialize)]
|
|
pub struct SpecificOTAVersionPath {
|
|
platform: OTAPlatform,
|
|
version: semver::Version,
|
|
}
|
|
|
|
/// Upload a new firmware update
|
|
pub async fn upload_firmware(
|
|
MultipartForm(form): MultipartForm<UploadForm>,
|
|
path: web::Path<SpecificOTAVersionPath>,
|
|
) -> HttpResult {
|
|
if ota_manager::update_exists(path.platform, &path.version)? {
|
|
return Ok(HttpResponse::Conflict()
|
|
.json("A firmware with the same version has already been uploaded on the platform!"));
|
|
}
|
|
|
|
let Some(file) = form.firmware.first() else {
|
|
return Ok(HttpResponse::BadRequest().json("No firmware specified!"));
|
|
};
|
|
|
|
if file.size == 0 {
|
|
return Ok(HttpResponse::BadRequest().json("Uploaded file is empty!"));
|
|
}
|
|
|
|
if file.size > constants::MAX_FIRMWARE_SIZE {
|
|
return Ok(HttpResponse::BadRequest().json("Uploaded file is too heavy!"));
|
|
}
|
|
|
|
let content = std::fs::read(file.file.path())?;
|
|
|
|
ota_manager::save_update(path.platform, &path.version, &content)?;
|
|
|
|
Ok(HttpResponse::Accepted().body("OTA update successfully saved."))
|
|
}
|
|
|
|
/// Download a firmware update
|
|
pub async fn download_firmware(path: web::Path<SpecificOTAVersionPath>) -> HttpResult {
|
|
if !ota_manager::update_exists(path.platform, &path.version)? {
|
|
return Ok(HttpResponse::NotFound().json("The requested firmware update was not found!"));
|
|
}
|
|
|
|
let firmware = ota_manager::get_ota_update(path.platform, &path.version)?;
|
|
|
|
Ok(HttpResponse::Ok()
|
|
.content_type("application/octet-stream")
|
|
.append_header((
|
|
"content-disposition",
|
|
format!(
|
|
"attachment; filename=\"{}-{}.bin\"",
|
|
path.platform, path.version
|
|
),
|
|
))
|
|
.body(firmware))
|
|
}
|
|
|
|
/// Delete an uploaded firmware update
|
|
pub async fn delete_update(path: web::Path<SpecificOTAVersionPath>) -> HttpResult {
|
|
if !ota_manager::update_exists(path.platform, &path.version)? {
|
|
return Ok(HttpResponse::NotFound().json("The requested firmware update was not found!"));
|
|
}
|
|
|
|
ota_manager::delete_update(path.platform, &path.version)?;
|
|
|
|
Ok(HttpResponse::Accepted().finish())
|
|
}
|
|
|
|
/// Get the list of all OTA updates
|
|
pub async fn list_all_ota() -> HttpResult {
|
|
Ok(HttpResponse::Ok().json(ota_manager::get_all_ota_updates()?))
|
|
}
|
|
|
|
#[derive(serde::Deserialize)]
|
|
pub struct ListOTAPath {
|
|
platform: OTAPlatform,
|
|
}
|
|
|
|
/// List OTA software updates for a given platform
|
|
pub async fn list_updates_platform(path: web::Path<ListOTAPath>) -> HttpResult {
|
|
let list = ota_manager::get_ota_updates_for_platform(path.platform)?;
|
|
|
|
Ok(HttpResponse::Ok().json(list))
|
|
}
|
|
|
|
#[derive(serde::Deserialize)]
|
|
pub struct SetDesiredDeviceVersion {
|
|
devices: Option<Vec<DeviceId>>,
|
|
platform: Option<OTAPlatform>,
|
|
version: semver::Version,
|
|
}
|
|
|
|
pub async fn set_desired_version(
|
|
actor: WebEnergyActor,
|
|
body: web::Json<SetDesiredDeviceVersion>,
|
|
) -> HttpResult {
|
|
if body.devices.is_none() && body.platform.is_none() {
|
|
return Ok(
|
|
HttpResponse::BadRequest().json("Must specify one filter to select target devices!")
|
|
);
|
|
}
|
|
|
|
let devices = actor.send(energy_actor::GetDeviceLists).await?;
|
|
|
|
for d in devices {
|
|
// Filter per platform
|
|
if let Some(p) = body.platform {
|
|
if d.info.reference != p.to_string() {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Filter per device
|
|
if let Some(ids) = &body.devices {
|
|
if !ids.contains(&d.id) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
actor
|
|
.send(energy_actor::SetDesiredVersion(
|
|
d.id,
|
|
Some(body.version.clone()),
|
|
))
|
|
.await??;
|
|
}
|
|
|
|
Ok(HttpResponse::Ok().finish())
|
|
}
|