package main import ( "encoding/json" "io" "net/http" "os" "os/exec" "sync" "time" "gitea.larvit.se/pwrpln/go_log" "github.com/joho/godotenv" ) func main() { // Load ENV from .env file err := godotenv.Load() if err != nil { tmpLog := go_log.GetLog() tmpLog.Info("Failed to load .env file!") } // Setting up logger log := go_log.GetLog() log.MinLogLvl = go_log.LogLvlFromStr(os.Getenv("LOG_LEVEL")) loc, err := time.LoadLocation(os.Getenv("LOG_LOCATION")) if err != nil { panic(err) } log.TimeLocation = loc // Open the JSON file for reading log.Info("Reading URLs and CMDs from file", "urls.json path", os.Getenv("URLS_CMDS_JSON_PATH")) file, err := os.Open(os.Getenv("URLS_CMDS_JSON_PATH")) if err != nil { log.Error("Error opening URLs and CMDs JSON file", "err", err.Error()) panic(err) } // Read the JSON data from the file jsonStr, err := io.ReadAll(file) if err != nil { log.Error("Error reading JSON string from file", "err", err.Error()) panic(err) } file.Close() var urlsToCmds map[string]string err = json.Unmarshal([]byte(jsonStr), &urlsToCmds) if err != nil { log.Error("Error unmarshalling JSON", "err", err.Error) panic(err) } http.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) { cmdStr, exists := urlsToCmds[req.URL.Path] if !exists { log.Verbose("Call to undefined URL", "URL", req.URL.Path) res.WriteHeader(404) res.Write([]byte("Not Found")) return } log.Verbose("Executing command", "cmdStr", cmdStr) // Set the response headers for SSE res.Header().Set("Content-Type", "text/event-stream") res.Header().Set("Cache-Control", "no-cache") res.Header().Set("Connection", "keep-alive") // Create the command cmd := exec.Command("/bin/sh", "-c", cmdStr) // Create a pipes to capture the command's output stdoutPipe, err := cmd.StdoutPipe() if err != nil { http.Error(res, "Internal Server Error", 500) log.Error("Could not create stdout pipe from command", "err", err.Error()) return } stderrPipe, err := cmd.StderrPipe() if err != nil { http.Error(res, "Internal Server Error", 500) log.Error("Could not create stderr pipe from command", "err", err.Error()) return } // Start the command err = cmd.Start() if err != nil { http.Error(res, "Internal Server Error", 500) log.Error("Could not start the command", "err", err.Error()) return } defer cmd.Wait() defer stdoutPipe.Close() defer stderrPipe.Close() var wg sync.WaitGroup wg.Add(1) go func() { buf := make([]byte, 1024) for { n, err := stdoutPipe.Read(buf) if err == io.EOF { break } if err != nil { log.Error("Error reading stdout from command", "err", err.Error()) break } log.Debug("std", "out", string(buf[:n])) res.Write(buf[:n]) flusher, ok := res.(http.Flusher) if ok { flusher.Flush() } } wg.Done() }() wg.Add(1) go func() { buf := make([]byte, 1024) for { n, err := stderrPipe.Read(buf) if err == io.EOF { break } if err != nil { log.Error("Error reading stderr from command", "err", err.Error()) break } log.Debug("std", "err", string(buf[:n])) res.Write([]byte("STDERR: ")) res.Write(buf[:n]) flusher, ok := res.(http.Flusher) if ok { flusher.Flush() } } wg.Done() }() wg.Wait() }) log.Info("Starting web server", "PORT", os.Getenv("PORT")) err = http.ListenAndServe(":"+os.Getenv("PORT"), nil) if err != nil { log.Error("Web server crashed", "err", err.Error()) } }