diff --git a/client/src/App.vue b/client/src/App.vue
index 3ceb988..435740a 100644
--- a/client/src/App.vue
+++ b/client/src/App.vue
@@ -91,7 +91,10 @@
{{ this.computeSizeCheckText() }}
-
+
{{ this.options.magicmode ? "" : "Choose image to install" }}
@@ -282,7 +285,9 @@ export default {
return "";
},
computeMagicButtonText() {
- return this.state == "MAGIC" ? "Cancel" : "Magic";
+ return this.state == "MAGIC" || this.state == "UPLOADING_MAGIC"
+ ? "Cancel"
+ : "Magic";
},
flashDirection() {
return this.flash.selectedMethod == 1 ? "left" : "right";
@@ -310,6 +315,8 @@ export default {
if (this.selectedMethod.id == 0 && this.selectedRebuildImage) return true;
if (this.selectedMethod.id == 1 && this.selectedRefactorImage)
return true;
+ if (this.selectedMethod.id == 2 && this.selectedUploadImage.file)
+ return true;
return false;
},
isTransferButtonVisible() {
@@ -363,6 +370,65 @@ export default {
}
});
},
+ async startMagicUpload() {
+ let self = this;
+ if (this.state == "IDLE") {
+ this.state = "UPLOADING_MAGIC";
+ await axios
+ .put(`/api/upload_magic_start`, {
+ filename: self.file.name,
+ size: self.file.size,
+ start_time: Date.now(),
+ })
+ .then(function (response) {
+ self.status = response.data["success"];
+ self.magicUploadLocalFile();
+ self.checkProgress();
+ });
+ } else {
+ this.apiCall("upload_cancel");
+ }
+ },
+ async magicUploadLocalFile() {
+ console.log("magig upload local file");
+ const CHUNK_SIZE = 3 * 1024 * 1024;
+ let self = this;
+ var reader = new FileReader();
+ var offset = 0;
+ var filesize = this.file.size;
+ console.log(filesize);
+
+ reader.onload = function () {
+ console.log("onload");
+ var result = reader.result;
+ var chunk = result;
+ axios
+ .post(`/api/upload_magic_chunk`, {
+ chunk: chunk,
+ })
+ .then(function (response) {
+ const status = response.data;
+ if (status.success && self.state == "UPLOADING_MAGIC") {
+ offset += CHUNK_SIZE;
+ if (offset <= filesize) {
+ var slice = self.file.slice(offset, offset + CHUNK_SIZE);
+ reader.readAsDataURL(slice);
+ } else {
+ offset = filesize;
+ self.apiCall("upload_finish");
+ }
+ } else {
+ self.apiCall("upload_cancel");
+ }
+ });
+ };
+
+ if (this.file) {
+ var slice = this.file.slice(offset, offset + CHUNK_SIZE);
+ reader.readAsDataURL(slice);
+ self.fileName = this.file.name;
+ }
+ },
async uploadSelected() {
let self = this;
if (this.state == "IDLE") {
@@ -379,7 +445,7 @@ export default {
self.checkProgress();
});
} else {
- this.apiCall('upload_cancel')
+ this.apiCall("upload_cancel");
}
},
async uploadLocalFile() {
@@ -405,10 +471,10 @@ export default {
reader.readAsDataURL(slice);
} else {
offset = filesize;
- self.apiCall('upload_finish');
+ self.apiCall("upload_finish");
}
} else {
- self.apiCall('upload_cancel')
+ self.apiCall("upload_cancel");
}
});
};
@@ -422,15 +488,19 @@ export default {
onMagicButtonClick() {
if (this.selectedMethod.id == 0) {
this.selectedGithubImage = this.selectedRebuildImage;
+ this.startMagic();
} else if (this.selectedMethod.id == 1) {
this.selectedGithubImage = this.selectedRefactorImage;
+ this.startMagic();
+ } else if (this.selectedMethod.id == 2) {
+ this.selectedGithubImage = this.selectedLocalImage;
+ this.startMagicUpload();
}
- this.startMagic();
},
async startMagic() {
let self = this;
if (this.state == "IDLE") {
- this.state = "MAGIC"
+ this.state = "MAGIC";
await axios
.put(`/api/start_magic`, {
filename: this.selectedGithubImage["name"],
@@ -478,6 +548,7 @@ export default {
"INSTALLING",
"BACKUPING",
"MAGIC",
+ "UPLOADING_MAGIC",
].includes(this.state)
) {
this.setProgress({ progress: data.progress });
diff --git a/reflash/go.mod b/reflash/go.mod
index e4f72fa..6f308f0 100644
--- a/reflash/go.mod
+++ b/reflash/go.mod
@@ -10,6 +10,7 @@ require (
require (
github.com/grafana/tail v0.0.0-20230510142333-77b18831edf0 // indirect
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
+ github.com/ulikunitz/xz v0.5.12 // indirect
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect
golang.org/x/sys v0.0.0-20220908164124-27713097b956 // indirect
gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect
diff --git a/reflash/go.sum b/reflash/go.sum
index de051a7..2e9eb8d 100644
--- a/reflash/go.sum
+++ b/reflash/go.sum
@@ -14,6 +14,8 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
+github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc h1:O9NuF4s+E/PvMIy+9IUZB9znFwUIXEWSstNjek6VpVg=
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4=
diff --git a/reflash/server.go b/reflash/server.go
index e558a9e..1e9a51f 100644
--- a/reflash/server.go
+++ b/reflash/server.go
@@ -19,6 +19,7 @@ import (
"github.com/grafana/tail"
"github.com/pelletier/go-toml/v2"
+ "github.com/ulikunitz/xz"
"golang.org/x/exp/slices"
)
@@ -88,15 +89,16 @@ type Settings struct {
}
const (
- IDLE = "IDLE"
- DOWNLOADING = "DOWNLOADING"
- UPLOADING = "UPLOADING"
- INSTALLING = "INSTALLING"
- BACKUPING = "BACKUPING"
- MAGIC = "MAGIC"
- FINISHED = "FINISHED"
- CANCELLED = "CANCELLED"
- ERROR = "ERROR"
+ IDLE = "IDLE"
+ DOWNLOADING = "DOWNLOADING"
+ UPLOADING = "UPLOADING"
+ INSTALLING = "INSTALLING"
+ BACKUPING = "BACKUPING"
+ MAGIC = "MAGIC"
+ UPLOADING_MAGIC = "UPLOADING_MAGIC"
+ FINISHED = "FINISHED"
+ CANCELLED = "CANCELLED"
+ ERROR = "ERROR"
)
const (
@@ -190,6 +192,8 @@ func ServerInit() {
http.HandleFunc("/api/cancel_backup", cancelBackup)
http.HandleFunc("/api/start_magic", startMagic)
http.HandleFunc("/api/cancel_magic", cancelMagic)
+ http.HandleFunc("/api/upload_magic_start", uploadMagicStart)
+ http.HandleFunc("/api/upload_magic_chunk", uploadMagicChunk)
http.HandleFunc("/api/get_progress", getProgress)
http.HandleFunc("/api/check_file_integrity", checkFileIntegrity)
http.HandleFunc("/api/run_install_finished_commands", runInstallFinishedCommands)
@@ -342,6 +346,72 @@ func uploadStart(w http.ResponseWriter, r *http.Request) {
sendResponse(w, nil)
}
+func uploadMagicStart(w http.ResponseWriter, r *http.Request) {
+ var data *Download = &Download{}
+ reqBody, _ := io.ReadAll(r.Body)
+ json.Unmarshal(reqBody, &data)
+
+ state.Filename = data.Filename
+ state.StartTime = data.StartTime
+ state.BytesNow = 0
+ state.BytesTotal = data.Size
+ state.State = UPLOADING_MAGIC
+
+ timeStart = time.Now()
+ logInfo("Starting magic upload at " + timeStart.Format("15:04:05"))
+ logInfo("Filename: " + state.Filename)
+
+ path := "/tmp/decompressed.img"
+ os.Create(path)
+ sendResponse(w, nil)
+}
+
+func uploadMagicChunk(w http.ResponseWriter, r *http.Request) {
+ var chunk *Chunk = &Chunk{}
+ reqBody, _ := io.ReadAll(r.Body)
+ json.Unmarshal(reqBody, &chunk)
+
+ decoded, err := base64.StdEncoding.DecodeString(chunk.Encoded[37:])
+
+ path := "/tmp/decompressed.img"
+
+ if state.State == CANCELLED {
+ response := map[string]bool{"success": false}
+ json.NewEncoder(w).Encode(response)
+ return
+ }
+
+ reader, err := xz.NewReader(bytes.NewReader(decoded))
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ var decompressedData bytes.Buffer
+ if _, err := io.Copy(&decompressedData, reader); err != nil {
+ log.Fatal("Failed to copy")
+ log.Fatal(err)
+ }
+
+ outFile, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0644)
+ if err != nil {
+ log.Fatal("Failed to open file")
+ log.Fatal(err)
+ }
+
+ if _, err := outFile.Write(decompressedData.Bytes()); err != nil {
+ log.Fatal("Failed write")
+ log.Fatal(err)
+ }
+ if err := outFile.Close(); err != nil {
+ log.Fatal(err)
+ }
+ state.BytesNow += len(decoded)
+ state.Progress = float64(state.BytesNow) * 100 / float64(state.BytesTotal)
+
+ response := map[string]bool{"success": true}
+ json.NewEncoder(w).Encode(response)
+}
+
func uploadChunk(w http.ResponseWriter, r *http.Request) {
var chunk *Chunk = &Chunk{}
reqBody, _ := io.ReadAll(r.Body)