mirror of
https://codeberg.org/PLG-Development/PLG-MuDiCS
synced 2026-07-06 00:47:09 +00:00
feat(control): store api (#3)
This commit is contained in:
+22
-1
@@ -6,6 +6,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
"plg-mudics/control/frontend"
|
"plg-mudics/control/frontend"
|
||||||
"plg-mudics/shared"
|
"plg-mudics/shared"
|
||||||
"time"
|
"time"
|
||||||
@@ -14,7 +15,25 @@ import (
|
|||||||
"github.com/labstack/echo/v4/middleware"
|
"github.com/labstack/echo/v4/middleware"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var storageFile string
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
path, err := getStoragePath()
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Failed to initialize storage path", "error", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
storageFile = filepath.Join(path, "storage.json")
|
||||||
|
// Ensure storage.json exists
|
||||||
|
if _, err := os.Stat(storageFile); os.IsNotExist(err) {
|
||||||
|
if err := os.WriteFile(storageFile, []byte("{}"), 0644); err != nil {
|
||||||
|
slog.Error("Failed to initialize storage.json", "error", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
e := echo.New()
|
e := echo.New()
|
||||||
|
|
||||||
// Serve the embedded SvelteKit frontend
|
// Serve the embedded SvelteKit frontend
|
||||||
@@ -26,6 +45,8 @@ func main() {
|
|||||||
// Servers all API endpoints, e.g. our custom logic
|
// Servers all API endpoints, e.g. our custom logic
|
||||||
apiGroup := e.Group("/api")
|
apiGroup := e.Group("/api")
|
||||||
apiGroup.GET("/ping", pingRoute)
|
apiGroup.GET("/ping", pingRoute)
|
||||||
|
apiGroup.GET("/storage", getStorageRoute)
|
||||||
|
apiGroup.POST("/storage", setStorageRoute)
|
||||||
|
|
||||||
port := "8080"
|
port := "8080"
|
||||||
|
|
||||||
@@ -38,7 +59,7 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
err := shared.OpenBrowserWindow("http://localhost:"+port, false, false)
|
err = shared.OpenBrowserWindow("http://localhost:"+port, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Failed to open browser window", "error", err)
|
slog.Error("Failed to open browser window", "error", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
meta {
|
||||||
|
name: Get Store
|
||||||
|
type: http
|
||||||
|
seq: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: http://localhost:8080/api/storage
|
||||||
|
body: none
|
||||||
|
auth: inherit
|
||||||
|
}
|
||||||
|
|
||||||
|
settings {
|
||||||
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
meta {
|
||||||
|
name: Set Store
|
||||||
|
type: http
|
||||||
|
seq: 3
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
url: http://localhost:8080/api/storage
|
||||||
|
body: json
|
||||||
|
auth: inherit
|
||||||
|
}
|
||||||
|
|
||||||
|
body:json {
|
||||||
|
{
|
||||||
|
"key": "value"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
settings {
|
||||||
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"plg-mudics/shared"
|
||||||
|
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getStorageRoute(ctx echo.Context) error {
|
||||||
|
data, err := os.ReadFile(storageFile)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Could not read storage file", "error", err)
|
||||||
|
return ctx.JSON(http.StatusInternalServerError, shared.ErrorResponse{Error: "Could not read storage file"})
|
||||||
|
}
|
||||||
|
|
||||||
|
var content interface{}
|
||||||
|
if err := json.Unmarshal(data, &content); err != nil {
|
||||||
|
slog.Error("Could not parse storage file", "error", err)
|
||||||
|
return ctx.JSON(http.StatusInternalServerError, shared.ErrorResponse{Error: "Could not parse storage file"})
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.JSON(http.StatusOK, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setStorageRoute(ctx echo.Context) error {
|
||||||
|
var payload interface{}
|
||||||
|
if err := ctx.Bind(&payload); err != nil {
|
||||||
|
return ctx.JSON(http.StatusBadRequest, shared.ErrorResponse{Error: "Invalid JSON payload"})
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.MarshalIndent(payload, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return ctx.JSON(http.StatusInternalServerError, shared.ErrorResponse{Error: "Failed to marshal storage file"})
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(storageFile, data, 0644); err != nil {
|
||||||
|
return ctx.JSON(http.StatusInternalServerError, shared.ErrorResponse{Error: "Failed to write storage file"})
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.JSON(http.StatusOK, map[string]interface{}{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStoragePath() (string, error) {
|
||||||
|
var storagePath string
|
||||||
|
|
||||||
|
home, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("unable to determine user home directory: %w", err)
|
||||||
|
}
|
||||||
|
storagePath = filepath.Join(home, ".local", "share", "plg-mudics", "control")
|
||||||
|
if err := os.MkdirAll(storagePath, os.ModePerm); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to create local config directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return storagePath, nil
|
||||||
|
}
|
||||||
+1
-1
@@ -95,7 +95,7 @@ func GetStoragePath() (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("unable to determine user home directory: %w", err)
|
return "", fmt.Errorf("unable to determine user home directory: %w", err)
|
||||||
}
|
}
|
||||||
storagePath = filepath.Join(home, ".local", "share", "plg-mudics")
|
storagePath = filepath.Join(home, ".local", "share", "plg-mudics", "display")
|
||||||
if err := os.MkdirAll(storagePath, os.ModePerm); err != nil {
|
if err := os.MkdirAll(storagePath, os.ModePerm); err != nil {
|
||||||
return "", fmt.Errorf("failed to create local config directory: %w", err)
|
return "", fmt.Errorf("failed to create local config directory: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-30
@@ -21,10 +21,6 @@ import (
|
|||||||
"plg-mudics/display/pkg"
|
"plg-mudics/display/pkg"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ErrorResponse struct {
|
|
||||||
Error string `json:"error"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var version string
|
var version string
|
||||||
var sseConnection chan string
|
var sseConnection chan string
|
||||||
var supportedExtensions = map[string]bool{
|
var supportedExtensions = map[string]bool{
|
||||||
@@ -136,7 +132,7 @@ func extractFilePathMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
|
|||||||
fullPath, exists, err := pkg.ResolveStorageFilePath(pathParam)
|
fullPath, exists, err := pkg.ResolveStorageFilePath(pathParam)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Warn("Failed to validate file path", "path", pathParam, "error", err)
|
slog.Warn("Failed to validate file path", "path", pathParam, "error", err)
|
||||||
return ctx.JSON(http.StatusBadRequest, ErrorResponse{Error: "Invalid file path"})
|
return ctx.JSON(http.StatusBadRequest, shared.ErrorResponse{Error: "Invalid file path"})
|
||||||
}
|
}
|
||||||
ctx.Set("fullPath", fullPath)
|
ctx.Set("fullPath", fullPath)
|
||||||
ctx.Set("fileExists", exists)
|
ctx.Set("fileExists", exists)
|
||||||
@@ -150,14 +146,14 @@ func shellCommandRoute(ctx echo.Context) error {
|
|||||||
}
|
}
|
||||||
if err := ctx.Bind(&commandInput); err != nil {
|
if err := ctx.Bind(&commandInput); err != nil {
|
||||||
slog.Error("Failed to parse shell command", "error", err)
|
slog.Error("Failed to parse shell command", "error", err)
|
||||||
return ctx.JSON(http.StatusBadRequest, ErrorResponse{Error: "Invalid JSON request"})
|
return ctx.JSON(http.StatusBadRequest, shared.ErrorResponse{Error: "Invalid JSON request"})
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := exec.Command("bash", "-c", commandInput.Command)
|
cmd := exec.Command("bash", "-c", commandInput.Command)
|
||||||
storagePath, err := pkg.GetStoragePath()
|
storagePath, err := pkg.GetStoragePath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Failed to get storage path", "error", err)
|
slog.Error("Failed to get storage path", "error", err)
|
||||||
return ctx.JSON(http.StatusInternalServerError, ErrorResponse{Error: "Internal server error"})
|
return ctx.JSON(http.StatusInternalServerError, shared.ErrorResponse{Error: "Internal server error"})
|
||||||
}
|
}
|
||||||
cmd.Dir = storagePath
|
cmd.Dir = storagePath
|
||||||
|
|
||||||
@@ -176,19 +172,19 @@ func keyboardInputRoute(ctx echo.Context) error {
|
|||||||
}
|
}
|
||||||
if err := ctx.Bind(&request); err != nil {
|
if err := ctx.Bind(&request); err != nil {
|
||||||
slog.Error("Failed to parse keyboard input", "error", err)
|
slog.Error("Failed to parse keyboard input", "error", err)
|
||||||
return ctx.JSON(http.StatusBadRequest, ErrorResponse{Error: "Invalid JSON request"})
|
return ctx.JSON(http.StatusBadRequest, shared.ErrorResponse{Error: "Invalid JSON request"})
|
||||||
}
|
}
|
||||||
|
|
||||||
code, ok := pkg.KeyboardEvents[request.Key]
|
code, ok := pkg.KeyboardEvents[request.Key]
|
||||||
if !ok {
|
if !ok {
|
||||||
slog.Error("Unsupported key", "key", request.Key)
|
slog.Error("Unsupported key", "key", request.Key)
|
||||||
return ctx.JSON(http.StatusBadRequest, ErrorResponse{Error: fmt.Sprintf("Unsupported key: %s", request.Key)})
|
return ctx.JSON(http.StatusBadRequest, shared.ErrorResponse{Error: fmt.Sprintf("Unsupported key: %s", request.Key)})
|
||||||
}
|
}
|
||||||
|
|
||||||
err := pkg.KeyboardInput(code)
|
err := pkg.KeyboardInput(code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Failed to send keyboard input", "key", request.Key, "error", err)
|
slog.Error("Failed to send keyboard input", "key", request.Key, "error", err)
|
||||||
return ctx.JSON(http.StatusInternalServerError, ErrorResponse{Error: "Failed to send keyboard input"})
|
return ctx.JSON(http.StatusInternalServerError, shared.ErrorResponse{Error: "Failed to send keyboard input"})
|
||||||
}
|
}
|
||||||
|
|
||||||
slog.Info("Keyboard input sent", "key", request.Key)
|
slog.Info("Keyboard input sent", "key", request.Key)
|
||||||
@@ -200,25 +196,25 @@ func uploadFileRoute(ctx echo.Context) error {
|
|||||||
|
|
||||||
// Ensure parent directories exist
|
// Ensure parent directories exist
|
||||||
if err := os.MkdirAll(filepath.Dir(fullPath), os.ModePerm); err != nil {
|
if err := os.MkdirAll(filepath.Dir(fullPath), os.ModePerm); err != nil {
|
||||||
return ctx.JSON(http.StatusInternalServerError, ErrorResponse{Error: "Failed to prepare storage directory"})
|
return ctx.JSON(http.StatusInternalServerError, shared.ErrorResponse{Error: "Failed to prepare storage directory"})
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.Get("fileExists").(bool) {
|
if ctx.Get("fileExists").(bool) {
|
||||||
return ctx.JSON(http.StatusConflict, ErrorResponse{Error: "File already exists"})
|
return ctx.JSON(http.StatusConflict, shared.ErrorResponse{Error: "File already exists"})
|
||||||
}
|
}
|
||||||
|
|
||||||
ext := strings.ToLower(filepath.Ext(fullPath))
|
ext := strings.ToLower(filepath.Ext(fullPath))
|
||||||
if !supportedExtensions[ext] {
|
if !supportedExtensions[ext] {
|
||||||
return ctx.JSON(http.StatusBadRequest, ErrorResponse{Error: fmt.Sprintf("Unsupported file extension: %s", ext)})
|
return ctx.JSON(http.StatusBadRequest, shared.ErrorResponse{Error: fmt.Sprintf("Unsupported file extension: %s", ext)})
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := io.ReadAll(ctx.Request().Body)
|
data, err := io.ReadAll(ctx.Request().Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ctx.JSON(http.StatusInternalServerError, ErrorResponse{Error: "Failed to read file body"})
|
return ctx.JSON(http.StatusInternalServerError, shared.ErrorResponse{Error: "Failed to read file body"})
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.WriteFile(fullPath, data, os.ModePerm); err != nil {
|
if err := os.WriteFile(fullPath, data, os.ModePerm); err != nil {
|
||||||
return ctx.JSON(http.StatusInternalServerError, ErrorResponse{Error: "Failed to save file"})
|
return ctx.JSON(http.StatusInternalServerError, shared.ErrorResponse{Error: "Failed to save file"})
|
||||||
}
|
}
|
||||||
|
|
||||||
slog.Info("File uploaded successfully", "path", fullPath)
|
slog.Info("File uploaded successfully", "path", fullPath)
|
||||||
@@ -229,13 +225,13 @@ func downloadFileRoute(ctx echo.Context) error {
|
|||||||
fullPath := ctx.Get("fullPath").(string)
|
fullPath := ctx.Get("fullPath").(string)
|
||||||
|
|
||||||
if !ctx.Get("fileExists").(bool) {
|
if !ctx.Get("fileExists").(bool) {
|
||||||
return ctx.JSON(http.StatusNotFound, ErrorResponse{Error: "File not found"})
|
return ctx.JSON(http.StatusNotFound, shared.ErrorResponse{Error: "File not found"})
|
||||||
}
|
}
|
||||||
|
|
||||||
err := ctx.File(fullPath)
|
err := ctx.File(fullPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Failed to serve file", "file", fullPath, "error", err)
|
slog.Error("Failed to serve file", "file", fullPath, "error", err)
|
||||||
return ctx.JSON(http.StatusInternalServerError, ErrorResponse{Error: "Internal server error"})
|
return ctx.JSON(http.StatusInternalServerError, shared.ErrorResponse{Error: "Internal server error"})
|
||||||
}
|
}
|
||||||
|
|
||||||
slog.Info("File downloaded successfully", "path", fullPath)
|
slog.Info("File downloaded successfully", "path", fullPath)
|
||||||
@@ -247,17 +243,17 @@ func openFileRoute(ctx echo.Context) error {
|
|||||||
fullPath := ctx.Get("fullPath").(string)
|
fullPath := ctx.Get("fullPath").(string)
|
||||||
|
|
||||||
if !ctx.Get("fileExists").(bool) {
|
if !ctx.Get("fileExists").(bool) {
|
||||||
return ctx.JSON(http.StatusNotFound, ErrorResponse{Error: "File not found"})
|
return ctx.JSON(http.StatusNotFound, shared.ErrorResponse{Error: "File not found"})
|
||||||
}
|
}
|
||||||
|
|
||||||
if sseConnection == nil {
|
if sseConnection == nil {
|
||||||
return ctx.JSON(http.StatusInternalServerError, ErrorResponse{Error: "Cant connect to display browser client"})
|
return ctx.JSON(http.StatusInternalServerError, shared.ErrorResponse{Error: "Cant connect to display browser client"})
|
||||||
}
|
}
|
||||||
|
|
||||||
err := resetView()
|
err := resetView()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Failed to reset view", "error", err)
|
slog.Error("Failed to reset view", "error", err)
|
||||||
return ctx.JSON(http.StatusInternalServerError, ErrorResponse{Error: "Failed to reset view"})
|
return ctx.JSON(http.StatusInternalServerError, shared.ErrorResponse{Error: "Failed to reset view"})
|
||||||
}
|
}
|
||||||
|
|
||||||
ext := strings.ToLower(filepath.Ext(fullPath))
|
ext := strings.ToLower(filepath.Ext(fullPath))
|
||||||
@@ -275,10 +271,10 @@ func openFileRoute(ctx echo.Context) error {
|
|||||||
err := pkg.OpenPresentation(fullPath)
|
err := pkg.OpenPresentation(fullPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Failed to open presentation", "file", pathParam, "error", err)
|
slog.Error("Failed to open presentation", "file", pathParam, "error", err)
|
||||||
return ctx.JSON(http.StatusInternalServerError, ErrorResponse{Error: "Failed to open presentation"})
|
return ctx.JSON(http.StatusInternalServerError, shared.ErrorResponse{Error: "Failed to open presentation"})
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return ctx.JSON(http.StatusBadRequest, ErrorResponse{Error: "Unsupported file type"})
|
return ctx.JSON(http.StatusBadRequest, shared.ErrorResponse{Error: "Unsupported file type"})
|
||||||
}
|
}
|
||||||
|
|
||||||
slog.Info("Successfully run file", "file", pathParam)
|
slog.Info("Successfully run file", "file", pathParam)
|
||||||
@@ -291,13 +287,13 @@ func showHTMLRoute(ctx echo.Context) error {
|
|||||||
}
|
}
|
||||||
if err := ctx.Bind(&request); err != nil {
|
if err := ctx.Bind(&request); err != nil {
|
||||||
slog.Error("Failed to parse request", "error", err)
|
slog.Error("Failed to parse request", "error", err)
|
||||||
return ctx.JSON(http.StatusBadRequest, ErrorResponse{Error: "Invalid JSON request"})
|
return ctx.JSON(http.StatusBadRequest, shared.ErrorResponse{Error: "Invalid JSON request"})
|
||||||
}
|
}
|
||||||
|
|
||||||
err := resetView()
|
err := resetView()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Failed to reset view", "error", err)
|
slog.Error("Failed to reset view", "error", err)
|
||||||
return ctx.JSON(http.StatusInternalServerError, ErrorResponse{Error: "Failed to reset view"})
|
return ctx.JSON(http.StatusInternalServerError, shared.ErrorResponse{Error: "Failed to reset view"})
|
||||||
}
|
}
|
||||||
|
|
||||||
sseConnection <- request.HTML
|
sseConnection <- request.HTML
|
||||||
@@ -318,13 +314,13 @@ func takeScreenshotRoute(ctx echo.Context) error {
|
|||||||
screenshotPath, err := pkg.TakeScreenshot()
|
screenshotPath, err := pkg.TakeScreenshot()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Failed to take screenshot", "error", err)
|
slog.Error("Failed to take screenshot", "error", err)
|
||||||
return ctx.JSON(http.StatusInternalServerError, ErrorResponse{Error: "Internal server error"})
|
return ctx.JSON(http.StatusInternalServerError, shared.ErrorResponse{Error: "Internal server error"})
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ctx.File(screenshotPath)
|
err = ctx.File(screenshotPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Failed to serve file", "file", screenshotPath, "error", err)
|
slog.Error("Failed to serve file", "file", screenshotPath, "error", err)
|
||||||
return ctx.JSON(http.StatusInternalServerError, ErrorResponse{Error: "Internal server error"})
|
return ctx.JSON(http.StatusInternalServerError, shared.ErrorResponse{Error: "Internal server error"})
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -334,19 +330,19 @@ func previewRoute(ctx echo.Context) error {
|
|||||||
fullPath := ctx.Get("fullPath").(string)
|
fullPath := ctx.Get("fullPath").(string)
|
||||||
exists := ctx.Get("fileExists").(bool)
|
exists := ctx.Get("fileExists").(bool)
|
||||||
if !exists {
|
if !exists {
|
||||||
return ctx.JSON(http.StatusNotFound, ErrorResponse{Error: "File not found"})
|
return ctx.JSON(http.StatusNotFound, shared.ErrorResponse{Error: "File not found"})
|
||||||
}
|
}
|
||||||
|
|
||||||
outputFilePath, err := pkg.GenerateFilePreview(fullPath)
|
outputFilePath, err := pkg.GenerateFilePreview(fullPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Failed to generate preview", "file", fullPath, "error", err)
|
slog.Error("Failed to generate preview", "file", fullPath, "error", err)
|
||||||
if errors.Is(err, pkg.ErrFileTypePreviewNotSupported) {
|
if errors.Is(err, pkg.ErrFileTypePreviewNotSupported) {
|
||||||
return ctx.JSON(http.StatusBadRequest, ErrorResponse{Error: "File type not supported for preview"})
|
return ctx.JSON(http.StatusBadRequest, shared.ErrorResponse{Error: "File type not supported for preview"})
|
||||||
}
|
}
|
||||||
if errors.Is(err, pkg.ErrFilePreviewToolsMissing) {
|
if errors.Is(err, pkg.ErrFilePreviewToolsMissing) {
|
||||||
return ctx.JSON(http.StatusInternalServerError, ErrorResponse{Error: "Required tools for file preview are missing"})
|
return ctx.JSON(http.StatusInternalServerError, shared.ErrorResponse{Error: "Required tools for file preview are missing"})
|
||||||
}
|
}
|
||||||
return ctx.JSON(http.StatusInternalServerError, ErrorResponse{Error: "Failed to generate preview"})
|
return ctx.JSON(http.StatusInternalServerError, shared.ErrorResponse{Error: "Failed to generate preview"})
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx.File(outputFilePath)
|
return ctx.File(outputFilePath)
|
||||||
|
|||||||
+3
-1
@@ -12,7 +12,9 @@ type CommandResponse struct {
|
|||||||
ExitCode int `json:"exitCode"`
|
ExitCode int `json:"exitCode"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ErrorResponse struct {
|
||||||
|
Error string `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
func RunShellCommand(cmd *exec.Cmd) CommandResponse {
|
func RunShellCommand(cmd *exec.Cmd) CommandResponse {
|
||||||
var stdout, stderr bytes.Buffer
|
var stdout, stderr bytes.Buffer
|
||||||
|
|||||||
Reference in New Issue
Block a user