commit dca8ac958a96eda7fbf75a996b2426c8dafc3945 Author: leafus Date: Sun Sep 29 19:15:37 2024 +0200 init diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..09d703f --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module main.go + +go 1.23.1 + +require github.com/joho/godotenv v1.5.1 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..d61b19e --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= diff --git a/main.go b/main.go new file mode 100644 index 0000000..fff6a22 --- /dev/null +++ b/main.go @@ -0,0 +1,158 @@ +package main + +import ( + "encoding/json" + "fmt" + "html/template" + "io" + "log" + "net/http" + "os" + "os/exec" + "path/filepath" + + "github.com/joho/godotenv" +) + +func main() { + err := godotenv.Load() + if err != nil { + log.Fatal("Error loading .env file") + } + + port := os.Getenv("PORT") + if port == "" { + port = "8080" + } + + http.HandleFunc("/", handleIndex) + http.HandleFunc("/upload", handleUpload) + http.HandleFunc("/delete", handleDelete) + http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("web/assets")))) + + fmt.Printf("Server starting on port %s\n", port) + log.Fatal(http.ListenAndServe(":"+port, nil)) +} + +func handleIndex(w http.ResponseWriter, r *http.Request) { + tmpl, err := template.ParseFiles("web/index.html") + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + err = tmpl.Execute(w, nil) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } +} + +func handleUpload(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + + if r.Method != http.MethodPost { + json.NewEncoder(w).Encode(map[string]string{"error": "Method not allowed"}) + return + } + + err := r.ParseMultipartForm(10 << 20) + if err != nil { + json.NewEncoder(w).Encode(map[string]string{"error": err.Error()}) + return + } + + file, handler, err := r.FormFile("file") + if err != nil { + json.NewEncoder(w).Encode(map[string]string{"error": err.Error()}) + return + } + defer file.Close() + + directory := r.FormValue("directory") + if directory == "" { + directory = "/" + } + + tempFileName := filepath.Join(os.TempDir(), handler.Filename) + tempFile, err := os.Create(tempFileName) + if err != nil { + json.NewEncoder(w).Encode(map[string]string{"error": err.Error()}) + return + } + defer os.Remove(tempFile.Name()) + defer tempFile.Close() + + _, err = io.Copy(tempFile, file) + if err != nil { + json.NewEncoder(w).Encode(map[string]string{"error": err.Error()}) + return + } + + s3ClientPath := filepath.Join(".", "bin", "s3-client") + configFilePath := filepath.Join(".", "bin", "s3config.toml") + cmd := exec.Command(s3ClientPath, "-config", configFilePath, "-directory", directory, "-file", tempFile.Name()) + + cmd.Args = append(cmd.Args, "-overwrite") + + output, err := cmd.CombinedOutput() + if err != nil { + log.Printf("Error uploading file: %s\n", output) + json.NewEncoder(w).Encode(map[string]string{ + "error": fmt.Sprintf("Error uploading file to S3: %s", output), + }) + return + } + + response := struct { + Message string `json:"message"` + Output string `json:"output"` + }{ + Message: fmt.Sprintf("File %s uploaded successfully", handler.Filename), + Output: string(output), + } + + json.NewEncoder(w).Encode(response) +} + +func handleDelete(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + + if r.Method != http.MethodPost { + json.NewEncoder(w).Encode(map[string]string{"error": "Method not allowed"}) + return + } + + err := r.ParseMultipartForm(10 << 20) + if err != nil { + json.NewEncoder(w).Encode(map[string]string{"error": err.Error()}) + return + } + + filename := r.FormValue("filename") + if filename == "" { + json.NewEncoder(w).Encode(map[string]string{"error": "Filename is required"}) + return + } + + s3ClientPath := filepath.Join(".", "bin", "s3-client") + configFilePath := filepath.Join(".", "bin", "s3config.toml") + cmd := exec.Command(s3ClientPath, "-config", configFilePath, "-delete", filename) + output, err := cmd.CombinedOutput() + if err != nil { + log.Printf("Error deleting file: %s\n", output) + json.NewEncoder(w).Encode(map[string]string{ + "error": fmt.Sprintf("Error deleting file from S3: %s", output), + }) + return + } + + response := struct { + Message string `json:"message"` + Output string `json:"output"` + }{ + Message: fmt.Sprintf("File %s deleted successfully from S3", filename), + Output: string(output), + } + + json.NewEncoder(w).Encode(response) +} diff --git a/web/assets/global.css b/web/assets/global.css new file mode 100644 index 0000000..91d6e1f --- /dev/null +++ b/web/assets/global.css @@ -0,0 +1,52 @@ +body { + background-color:#121212; + color: #fff; + font-family: Arial, Helvetica, sans-serif; +} + +.header { + margin-top: 10px; + margin-bottom: 20px; + margin-left: 10px; + font-size: 20px; +} + +.button { + margin-top: 20px; + width: 450px; +} + +input { + background-color: #161616; + border: none; + padding: 10px; + outline: none; + border-radius: 3px; + color: #fff; +} + +button { + background-color: #161616; + border: none; + padding: 10px; + outline: none; + border-radius: 3px; + color: #fff; +} + +button:hover { + cursor: pointer; +} + +a { + color: #fff; +} + +a:hover { + color: violet; +} + +fieldset { + border-color: #161616; + border-style: solid; +} \ No newline at end of file diff --git a/web/assets/index.js b/web/assets/index.js new file mode 100644 index 0000000..de858d8 --- /dev/null +++ b/web/assets/index.js @@ -0,0 +1,59 @@ +const uploadForm = document.getElementById('uploadForm'); +const uploadStatus = document.getElementById('uploadStatus'); +const commandOutput = document.getElementById('commandOutput'); + +uploadForm.addEventListener('submit', async (e) => { + e.preventDefault(); + uploadStatus.textContent = 'Uploading...'; + commandOutput.textContent = ''; + + const formData = new FormData(uploadForm); + + try { + const response = await fetch('/upload', { + method: 'POST', + body: formData + }); + + const result = await response.json(); + + if (result.error) { + throw new Error(result.error); + } else { + uploadStatus.textContent = result.message; + commandOutput.textContent = result.output; + } + } catch (error) { + uploadStatus.textContent = `Error: ${error.message}`; + } +}); + +const deleteForm = document.getElementById('deleteForm'); +const deleteStatus = document.getElementById('deleteStatus'); +const deleteOutput = document.getElementById('deleteOutput'); + +deleteForm.addEventListener('submit', async (e) => { + e.preventDefault(); + deleteStatus.textContent = 'Deleting...'; + deleteOutput.textContent = ''; + + const formData = new FormData(deleteForm); + + try { + const response = await fetch('/delete', { + method: 'POST', + body: formData + }); + + const result = await response.json(); + + if (result.error) { + throw new Error(result.error); + } else { + deleteStatus.textContent = result.message; + deleteOutput.textContent = result.output; + } + } catch (error) { + deleteStatus.textContent = `Error: ${error.message}`; + } +}); \ No newline at end of file diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..0b0b248 --- /dev/null +++ b/web/index.html @@ -0,0 +1,64 @@ + + + + + + + S3-Client Web Wrapper + + + +
S3-Client WebGUI
+ +
+ File Selection +
+ + +
+ +
+
+ +
+ Delete +
+ + +
+
+ +
+ Terminal +
+

+      
+
+

+      
+
+ +
+ [ s3-client ] - + [ s3-client-web ] - Licensed under MIT +
+ + + +