diff --git a/central_backend/src/server/servers.rs b/central_backend/src/server/servers.rs index 700a1aa..2a979f0 100644 --- a/central_backend/src/server/servers.rs +++ b/central_backend/src/server/servers.rs @@ -191,13 +191,16 @@ pub async fn secure_server(energy_actor: EnergyActorAddr) -> anyhow::Result<()> "/web_api/ota/{platform}/{version}", web::post().to(ota_controller::upload_firmware), ) + .route( + "/web_api/ota/{platform}/{version}", + web::get().to(ota_controller::download_firmware), + ) + // TODO : delete an OTA file .route("/web_api/ota", web::get().to(ota_controller::list_all_ota)) .route( "/web_api/ota/{platform}", web::get().to(ota_controller::list_updates_platform), ) - // TODO : download a OTA file - // TODO : delete an OTA file .route( "/web_api/ota/set_desired_version", web::post().to(ota_controller::set_desired_version), diff --git a/central_backend/src/server/web_api/ota_controller.rs b/central_backend/src/server/web_api/ota_controller.rs index e619288..278af05 100644 --- a/central_backend/src/server/web_api/ota_controller.rs +++ b/central_backend/src/server/web_api/ota_controller.rs @@ -20,14 +20,15 @@ pub struct UploadForm { } #[derive(serde::Deserialize)] -pub struct UploadPath { +pub struct SpecificOTAVersionPath { platform: OTAPlatform, version: semver::Version, } +/// Upload a new firmware update pub async fn upload_firmware( MultipartForm(form): MultipartForm, - path: web::Path, + path: web::Path, ) -> HttpResult { if ota_manager::update_exists(path.platform, &path.version)? { return Ok(HttpResponse::Conflict() @@ -53,6 +54,26 @@ pub async fn upload_firmware( Ok(HttpResponse::Accepted().body("OTA update successfully saved.")) } +/// Download a firmware update +pub async fn download_firmware(path: web::Path) -> HttpResult { + if !ota_manager::update_exists(path.platform, &path.version)? { + return Ok(HttpResponse::NotFound().json("The requested firmware 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)) +} + /// Get the list of all OTA updates pub async fn list_all_ota() -> HttpResult { Ok(HttpResponse::Ok().json(ota_manager::get_all_ota_updates()?)) diff --git a/central_frontend/src/api/OTAApi.ts b/central_frontend/src/api/OTAApi.ts index 962c761..0d1f180 100644 --- a/central_frontend/src/api/OTAApi.ts +++ b/central_frontend/src/api/OTAApi.ts @@ -37,6 +37,13 @@ export class OTAAPI { }); } + /** + * Get the link to download an OTA update + */ + static DownloadOTAUpdateURL(platform: string, version: string): string { + return APIClient.backendURL() + `/ota/${platform}/${version}`; + } + /** * Get the list of OTA updates */ diff --git a/central_frontend/src/routes/OTARoute.tsx b/central_frontend/src/routes/OTARoute.tsx index d248c7a..e8c8b52 100644 --- a/central_frontend/src/routes/OTARoute.tsx +++ b/central_frontend/src/routes/OTARoute.tsx @@ -1,3 +1,5 @@ +import DownloadIcon from "@mui/icons-material/Download"; +import FileUploadIcon from "@mui/icons-material/FileUpload"; import { IconButton, Paper, @@ -9,13 +11,13 @@ import { TableRow, Tooltip, } from "@mui/material"; -import { SolarEnergyRouteContainer } from "../widgets/SolarEnergyRouteContainer"; -import FileUploadIcon from "@mui/icons-material/FileUpload"; -import { UploadUpdateDialog } from "../dialogs/UploadUpdateDialog"; +import { filesize } from "filesize"; import React from "react"; import { OTAAPI, OTAUpdate } from "../api/OTAApi"; +import { UploadUpdateDialog } from "../dialogs/UploadUpdateDialog"; import { AsyncWidget } from "../widgets/AsyncWidget"; -import { filesize } from "filesize"; +import { RouterLink } from "../widgets/RouterLink"; +import { SolarEnergyRouteContainer } from "../widgets/SolarEnergyRouteContainer"; export function OTARoute(): React.ReactElement { const [list, setList] = React.useState(); @@ -97,6 +99,7 @@ function _OTAList(p: { list: OTAUpdate[] }): React.ReactElement { Platform Version File size + @@ -105,6 +108,17 @@ function _OTAList(p: { list: OTAUpdate[] }): React.ReactElement { {row.platform} {row.version} {filesize(row.file_size)} + + + + + + + + + ))}