@@ -0,0 +1,51 @@
|
||||
package com.rdkr.tide_display.backend.firmware.controller;
|
||||
|
||||
import com.rdkr.tide_display.backend.gcp.StorageService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.val;
|
||||
import org.apache.coyote.BadRequestException;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/files")
|
||||
@RequiredArgsConstructor
|
||||
public class FilesController {
|
||||
|
||||
private final StorageService storageService;
|
||||
|
||||
@GetMapping("/{version:\\d\\.\\d\\.\\d}/{filename:.+}")
|
||||
@ResponseBody
|
||||
public ResponseEntity<byte[]> download(@PathVariable String version, @PathVariable String filename) {
|
||||
byte[] file = storageService.download(version, filename);
|
||||
|
||||
if (file == null) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
return ResponseEntity
|
||||
.ok()
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"")
|
||||
.body(file);
|
||||
}
|
||||
|
||||
@PostMapping()
|
||||
public String upload(@RequestParam("file") MultipartFile file, RedirectAttributes redirectAttributes)
|
||||
throws Exception {
|
||||
val version = storageService.upload(file);
|
||||
if (version == null) {
|
||||
throw new BadRequestException("Invalid firmware file");
|
||||
}
|
||||
redirectAttributes.addFlashAttribute("message", "You successfully uploaded " + file.getOriginalFilename() + "!");
|
||||
return "redirect:/v1/versions/" + version;
|
||||
}
|
||||
}
|
@@ -27,7 +27,7 @@ public class FirmwareServiceImpl implements FirmwareService {
|
||||
public Optional<Firmware> getFirmwareVersion(String version) {
|
||||
return
|
||||
storageService.getFirmwareVersions().stream()
|
||||
//.filter(firmware -> firmware.version().equals(version))
|
||||
.filter(firmware -> firmware.getVersion().equals(version))
|
||||
.findFirst();
|
||||
}
|
||||
}
|
||||
|
@@ -22,6 +22,12 @@ public class Firmware {
|
||||
|
||||
private String eTag;
|
||||
|
||||
private int flashSize;
|
||||
|
||||
private String espIdf;
|
||||
|
||||
private String compiled;
|
||||
|
||||
private OffsetDateTime lastModified;
|
||||
|
||||
public String getVersion() {
|
||||
|
@@ -1,7 +1,13 @@
|
||||
package com.rdkr.tide_display.backend.gcp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
public interface StorageService {
|
||||
List<Firmware> getFirmwareVersions();
|
||||
|
||||
byte[] download(String version, String filename);
|
||||
|
||||
String upload(MultipartFile file) throws IOException;
|
||||
}
|
||||
|
@@ -0,0 +1,15 @@
|
||||
package com.rdkr.tide_display.backend.gcp.bean;
|
||||
|
||||
import com.google.cloud.storage.Storage;
|
||||
import com.google.cloud.storage.StorageOptions;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class GoogleCloudBean {
|
||||
|
||||
@Bean
|
||||
public Storage getStorage(){
|
||||
return StorageOptions.getDefaultInstance().getService();
|
||||
}
|
||||
}
|
@@ -1,35 +1,56 @@
|
||||
package com.rdkr.tide_display.backend.gcp.service;
|
||||
|
||||
import com.google.cloud.storage.BlobInfo;
|
||||
import com.google.cloud.storage.Storage;
|
||||
import com.google.cloud.storage.Storage.BlobListOption;
|
||||
import com.google.cloud.storage.StorageOptions;
|
||||
import com.rdkr.tide_display.backend.gcp.Firmware;
|
||||
import com.rdkr.tide_display.backend.gcp.StorageService;
|
||||
import jakarta.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.val;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class StorageServiceImpl implements StorageService {
|
||||
|
||||
final Storage storage;
|
||||
final String bucketName = "ec804bd8-38d2-4fcc-8f04-5addb55b3c90";
|
||||
|
||||
private String extract(byte[] bytes, int start, int length) {
|
||||
val result = new StringBuilder();
|
||||
for (var pos = start; pos < Math.min(start + length, bytes.length); pos++) {
|
||||
result.append((char) bytes[pos]);
|
||||
}
|
||||
return result.toString().trim();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Firmware> getFirmwareVersions() {
|
||||
val result = new ArrayList<Firmware>();
|
||||
val storage = StorageOptions.getDefaultInstance().getService();
|
||||
String bucketName = "ec804bd8-38d2-4fcc-8f04-5addb55b3c90";
|
||||
val bucket = storage.list(bucketName, BlobListOption.currentDirectory(), BlobListOption.prefix("firmware/"));
|
||||
for (val directory : bucket.iterateAll()) {
|
||||
val files = storage.list(bucketName, BlobListOption.currentDirectory(), BlobListOption.prefix(directory.getName()));
|
||||
for (val file : files.iterateAll()) {
|
||||
val name = file.getName();
|
||||
if (name.endsWith(".bin")) {
|
||||
val bytes = file.getContent();
|
||||
val flashSizeCode = bytes[3] >> 4;
|
||||
val flashSize = (int) Math.pow(2, flashSizeCode);
|
||||
val espIdf = extract(bytes, 144, 30);
|
||||
val compiled = extract(bytes, 128, 15) + " " + extract(bytes, 112, 15);
|
||||
val version = directory.getName().replace("firmware/", "").replace("/", "").split("\\.");
|
||||
if (version.length == 3) {
|
||||
int major = Integer.parseInt(version[0]);
|
||||
int minor = Integer.parseInt(version[1]);
|
||||
int patch = Integer.parseInt(version[2]);
|
||||
result.add(new Firmware(major, minor, patch, "/files/" + major + "." + minor + "." + patch + "/firmware.bin", file.getEtag(), file.getUpdateTimeOffsetDateTime()));
|
||||
result.add(
|
||||
new Firmware(major, minor, patch, "/files/" + major + "." + minor + "." + patch + "/firmware.bin",
|
||||
file.getEtag(), flashSize, espIdf, compiled, file.getUpdateTimeOffsetDateTime()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,4 +67,21 @@ public class StorageServiceImpl implements StorageService {
|
||||
|
||||
return result.reversed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] download(String version, String filename) {
|
||||
val blob = storage.get(bucketName, "firmware/" + version + "/" + filename);
|
||||
return blob != null ? blob.getContent() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String upload(MultipartFile file) throws IOException {
|
||||
val bytes = file.getBytes();
|
||||
if (bytes[0] != -23) {
|
||||
return null;
|
||||
}
|
||||
val version = extract(bytes, 48, 30);
|
||||
storage.create(BlobInfo.newBuilder(bucketName, "firmware/" + version + "/firmware.bin").build(), bytes);
|
||||
return version;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user