From 266110a51feb5d12fce433bcd170daf8013ab817 Mon Sep 17 00:00:00 2001 From: Jesse Malotaux Date: Sat, 12 Apr 2025 15:28:21 +0200 Subject: [PATCH] Backend update: Macro translation and refactor of saving and playing macros. --- be/app/api.go | 4 ++ be/app/device.go | 8 ++- be/app/helper/device-helper.go | 2 + be/app/helper/env-helper.go | 87 ++++++++++++++++++++------- be/app/helper/macro-helper.go | 49 +++++++++++---- be/app/helper/translation-helper.go | 93 +++++++++++++++++++++++++++++ be/app/log.go | 11 ++-- be/app/macro.go | 37 +++++++++++- be/app/structs/macro-struct.go | 13 ++++ be/main.go | 6 +- 10 files changed, 269 insertions(+), 41 deletions(-) create mode 100644 be/app/helper/translation-helper.go diff --git a/be/app/api.go b/be/app/api.go index c06ef98..3809a52 100644 --- a/be/app/api.go +++ b/be/app/api.go @@ -2,6 +2,7 @@ package app import ( "be/app/helper" + "log" "mime" "net/http" "path/filepath" @@ -29,6 +30,9 @@ func ApiGet(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" { file = "../public" + r.URL.Path // request } + + log.Println("ApiGet file: ", file) + contentType := mime.TypeByExtension(filepath.Ext(file)) // get content type if contentType != "" { diff --git a/be/app/device.go b/be/app/device.go index 7a2fb58..88e05c3 100644 --- a/be/app/device.go +++ b/be/app/device.go @@ -157,9 +157,13 @@ func PingLink(w http.ResponseWriter, r *http.Request) { key, keyErr := os.ReadFile("devices/" + req.Uuid + ".key") pin, pinErr := os.ReadFile("devices/" + req.Uuid + ".tmp") + // MCRMLog("PingLink UUID: ", req.Uuid) + // MCRMLog("PingLink Key: ", string(key), "; Pin: ", string(pin)) + encryptedKey, encErr := helper.EncryptAES(string(pin), string(key)) if keyErr == nil && pinErr == nil && encErr == nil { + MCRMLog("PINGLINK FIXED") w.Header().Set("Content-Type", "application/json") w.Write([]byte(encryptedKey)) return @@ -255,9 +259,9 @@ func Handshake(w http.ResponseWriter, r *http.Request) { decryptShake, _ := helper.DecryptAES(deviceKey, req.Shake) - if decryptShake == getDateStr() { - os.Remove("devices/" + req.Uuid + ".tmp") + helper.RemovePinFile(req.Uuid) + if decryptShake == getDateStr() { json.NewEncoder(w).Encode(true) return } else { diff --git a/be/app/helper/device-helper.go b/be/app/helper/device-helper.go index d850952..b0cd334 100644 --- a/be/app/helper/device-helper.go +++ b/be/app/helper/device-helper.go @@ -19,6 +19,8 @@ func TempPinFile(Uuid string, pin string) (bool, error) { return true, nil } +func RemovePinFile(Uuid string) error { return os.Remove("devices/" + Uuid + ".tmp") } + func CheckPinFile(Uuid string) bool { _, err := os.Stat("devices/" + Uuid + ".tmp") return err == nil diff --git a/be/app/helper/env-helper.go b/be/app/helper/env-helper.go index 08f0b5a..2e895b6 100644 --- a/be/app/helper/env-helper.go +++ b/be/app/helper/env-helper.go @@ -1,49 +1,92 @@ package helper import ( + "encoding/json" "log" "net" "os" "strconv" - - "github.com/joho/godotenv" + "strings" ) +var configPath = "../public/config.js" + func EnvGet(key string) string { - envFile := "../.env" - fileExists := func() bool { - _, err := os.Stat(envFile) - return err == nil + if !configFileExists() { + createConfigFile(configPath) + checkFeDevDir() } - if !fileExists() { - createEnvFile(envFile) - } - err := godotenv.Load(envFile) + + data, err := os.ReadFile(configPath) if err != nil { + log.Println("Error reading config.js:", err) return "" } - return os.Getenv("VITE_" + key) + + raw := strings.TrimSpace(string(data)) + raw = strings.TrimPrefix(raw, "window.__CONFIG__ = ") + raw = strings.TrimSuffix(raw, ";") + + var config map[string]string + if err := json.Unmarshal([]byte(raw), &config); err != nil { + log.Println("Error parsing config.js:", err) + return "" + } + + return config[key] } -func createEnvFile(filename string) { - log.Println("Creating .env file...") - file, err := os.Create(filename) +func configFileExists() bool { + _, err := os.Stat(configPath) + return err == nil +} + +func checkFeDevDir() { + _, err := os.Stat("../fe") + if err != nil { - log.Println(err) + return } - defer file.Close() - // You can add some default values to the .env file here if needed - // For example: - port, err := findOpenPort() + + copyConfigToFe() +} + +func copyConfigToFe() { + data, err := os.ReadFile("../public/config.js") + + if err != nil { + log.Println("Error reading config.js:", err) + return + } + + if err := os.WriteFile("../fe/config.js", data, 0644); err != nil { + log.Println("Error writing config.js:", err) + } +} + +func createConfigFile(filename string) { + port, _ := findOpenPort() saltKey := GenerateKey() salt := saltKey[:28] iv := GenerateRandomIntegerString(16) - log.Println(err, saltKey, iv) + config := map[string]string{ + "MCRM__PORT": port, + "MCRM__SALT": salt, + "MCRM__IV": iv, + } - _, err = file.WriteString("VITE_MCRM__PORT=" + string(port) + "\nVITE_MCRM__SALT=" + salt + "\nVITE_MCRM__IV=" + iv) + jsonData, err := json.MarshalIndent(config, "", " ") if err != nil { - log.Fatal(err) + log.Println("Error creating config JSON:", err) + return + } + jsData := "window.__CONFIG__ = " + string(jsonData) + ";" + + log.Println("Created JS config:", jsData) + + if err := os.WriteFile(filename, []byte(jsData), 0644); err != nil { + log.Println("Error writing config.js:", err) } } diff --git a/be/app/helper/macro-helper.go b/be/app/helper/macro-helper.go index b06c430..89805b8 100644 --- a/be/app/helper/macro-helper.go +++ b/be/app/helper/macro-helper.go @@ -3,13 +3,13 @@ package helper import ( "encoding/json" "errors" + "fmt" + "log" "os" "regexp" "strings" "time" - "be/app/structs" - "github.com/go-vgo/robotgo" ) @@ -33,7 +33,7 @@ func FormatMacroFileName(s string) string { return s } -func ReadMacroFile(filename string) (steps []structs.Step, err error) { +func ReadMacroFile(filename string) (steps []map[string]interface{}, err error) { content, err := os.ReadFile(filename) if err != nil { @@ -45,19 +45,48 @@ func ReadMacroFile(filename string) (steps []structs.Step, err error) { return steps, err } -func RunMacroSteps(steps []structs.Step) error { +func RunMacroSteps(steps []map[string]interface{}) error { for _, step := range steps { - switch step.Type { + switch step["type"] { case "key": - err := robotgo.KeyToggle(step.Key, step.Direction) - if err != nil { - return errors.New("RunMacroSteps KeyToggle Error: " + err.Error()) + keyCode := step["code"].(string) + if strings.Contains(keyCode, "|") { + keyCode = handleToggleCode(keyCode, step["direction"].(string)) } + log.Println("keycode", keyCode, step["direction"].(string)) + robotgo.KeyToggle(keyCode, step["direction"].(string)) case "delay": - time.Sleep(time.Duration(step.Location) * time.Millisecond) + log.Println("delay", step["value"].(float64)) + time.Sleep(time.Duration(step["value"].(float64)) * time.Millisecond) default: - return errors.New("RunMacroSteps Unknown step type:" + step.Type) + return errors.New("RunMacroSteps Unknown step type: %v" + fmt.Sprint(step["type"])) } } + log.Println("-----") return nil } + +var toggleCodes = map[string]bool{} + +func handleToggleCode(keyCode string, direction string) string { + splitCodes := strings.Split(keyCode, "|") + + if direction == "down" { + if _, ok := toggleCodes[splitCodes[0]]; !ok { + toggleCodes[splitCodes[0]] = true + return splitCodes[0] + } + return splitCodes[1] + } + + if direction == "up" { + if toggleCodes[splitCodes[0]] { + toggleCodes[splitCodes[0]] = false + return splitCodes[0] + } + delete(toggleCodes, splitCodes[0]) + return splitCodes[1] + } + + return "" +} diff --git a/be/app/helper/translation-helper.go b/be/app/helper/translation-helper.go new file mode 100644 index 0000000..4cbf5ea --- /dev/null +++ b/be/app/helper/translation-helper.go @@ -0,0 +1,93 @@ +package helper + +import "strings" + +func Translate(code string) string { + translations := map[string]string{ + "ArrowUp": "up", + "ArrowDown": "down", + "ArrowRight": "right", + "ArrowLeft": "left", + "Meta": "cmd", + "MetaLeft": "lcmd", + "MetaRight": "rcmd", + "Alt": "alt", + "AltLeft": "lalt", + "AltRight": "ralt", + "Control": "ctrl", + "ControlLeft": "lctrl", + "ControlRight": "rctrl", + "Shift": "shift", + "ShiftLeft": "lshift", + "ShiftRight": "rshift", + "AudioVolumeMute": "audio_mute", + "AudioVolumeDown": "audio_vol_down", + "AudioVolumeUp": "audio_vol_up", + "MediaTrackPrevious": "audio_prev", + "MediaTrackNext": "audio_next", + "MediaPlayPause": "audio_play|audio_pause", + "Numpad0": "num0", + "Numpad1": "num1", + "Numpad2": "num2", + "Numpad3": "num3", + "Numpad4": "num4", + "Numpad5": "num5", + "Numpad6": "num6", + "Numpad7": "num7", + "Numpad8": "num8", + "Numpad9": "num9", + "NumLock": "num_lock", + "NumpadDecimal": "num.", + "NumpadAdd": "num+", + "NumpadSubtract": "num-", + "NumpadMultiply": "num*", + "NumpadDivide": "num/", + "NumpadEnter": "num_enter", + "Clear": "num_clear", + } + + if translations[code] == "" { + return strings.ToLower(code) + } + + return translations[code] +} + +// Redundant translation because tolower can be used +// "Backspace": "backspace", +// "Delete": "delete", +// "Enter": "enter", +// "Tab": "tab", +// "Escape": "esc", +// "Home": "home", +// "End": "end", +// "PageUp": "pageup", +// "PageDown": "pagedown", +// "F1": "f1", +// "F2": "f2", +// "F3": "f3", +// "F4": "f4", +// "F5": "f5", +// "F6": "f6", +// "F7": "f7", +// "F8": "f8", +// "F9": "f9", +// "F10": "f10", +// "F11": "f11", +// "F12": "f12", +// "F13": "f13", +// "F14": "f14", +// "F15": "f15", +// "F16": "f16", +// "F17": "f17", +// "F18": "f18", +// "F19": "f19", +// "F20": "f20", +// "F21": "f21", +// "F22": "f22", +// "F23": "f23", +// "F24": "f24", +// "CapsLock": "capslock", +// "Space": "space", +// "PrintScreen": "printscreen", +// "Insert": "insert", diff --git a/be/app/log.go b/be/app/log.go index d6d7879..cd0ef88 100644 --- a/be/app/log.go +++ b/be/app/log.go @@ -1,7 +1,6 @@ package app import ( - "fmt" "log" "os" ) @@ -10,13 +9,17 @@ var logFile *os.File func MCRMLogInit() { var err error + // Open or create the log file with append permissions logFile, err = os.OpenFile("log.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { - log.Fatal(err) + log.Fatal(err) // Exit if we can't open the log file } + + // Optionally set log to write to file in addition to standard log output + // log.SetOutput(logFile) } func MCRMLog(v ...interface{}) { - log.Println(v...) - fmt.Fprintln(logFile, v...) + log.Println(v...) // Logs to terminal as well + // fmt.Fprintln(logFile, v...) // Logs to log file } diff --git a/be/app/macro.go b/be/app/macro.go index d987dae..b6dd392 100644 --- a/be/app/macro.go +++ b/be/app/macro.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "io" + "log" "net/http" "os" "path/filepath" @@ -28,12 +29,19 @@ func SaveMacro(w http.ResponseWriter, r *http.Request) { return } - stepsJSON, err := json.Marshal(newMacro.Steps) + simplifiedSteps := make([]map[string]interface{}, 0) + for _, step := range newMacro.Steps { + simplifiedSteps = append(simplifiedSteps, simplifyMacro(step)) + } + + stepsJSON, err := json.Marshal(simplifiedSteps) if err != nil { MCRMLog("SaveMacro Marshal Error: ", err) return } + log.Println(simplifiedSteps) + err = os.WriteFile("../macros/"+helper.FormatMacroFileName(newMacro.Name)+".json", stepsJSON, 0644) if err != nil { MCRMLog("SaveMacro WriteFile Error: ", err) @@ -41,11 +49,36 @@ func SaveMacro(w http.ResponseWriter, r *http.Request) { } } +func simplifyMacro(step structs.Step) map[string]interface{} { + simplified := make(map[string]interface{}) + + simplified["type"] = step.Type + + switch step.Type { + case "delay": + simplified["value"] = step.Value + case "key": + keyCode := step.Code + + if keyCode == "" { + keyCode = step.Key + } else if strings.Contains(keyCode, "Key") { + keyCode = strings.Replace(keyCode, "Key", "", 1) + } + + simplified["code"] = helper.Translate(keyCode) + simplified["direction"] = step.Direction + } + + return simplified +} + func ListMacros(w http.ResponseWriter, r *http.Request) { dir := "../macros" files, err := os.ReadDir(dir) if err != nil { MCRMLog("ListMacros ReadDir Error: ", err) + json.NewEncoder(w).Encode(false) return } @@ -78,7 +111,7 @@ func PlayMacro(data string, w http.ResponseWriter, r *http.Request) { } macro := req.Macro - + log.Println("PlayMacro: ", macro) var filename = helper.FormatMacroFileName(macro) var filepath = fmt.Sprintf("../macros/%s.json", filename) diff --git a/be/app/structs/macro-struct.go b/be/app/structs/macro-struct.go index e91a51c..71120b4 100644 --- a/be/app/structs/macro-struct.go +++ b/be/app/structs/macro-struct.go @@ -22,3 +22,16 @@ type MacroInfo struct { Name string `json:"name"` Macroname string `json:"macroname"` } + +type MacroKey struct { + Type string `json:"type"` + Key string `json:"key"` + Code string `json:"code"` + Location int `json:"location"` + Direction string `json:"direction"` +} + +type MacroDelay struct { + Type string `json:"type"` + Value int `json:"value"` +} diff --git a/be/main.go b/be/main.go index ea7cb7a..9ca821f 100644 --- a/be/main.go +++ b/be/main.go @@ -1,6 +1,7 @@ package main import ( + "log" "net/http" "be/app" @@ -15,10 +16,13 @@ func main() { } http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + log.Println("HANDLEFUNC", r.URL.Path) apiInit(w, r) }) - helper.OpenBrowser("http://localhost:" + helper.EnvGet("MCRM__PORT")) + log.Println("Listening on http://localhost:" + helper.EnvGet("MCRM__PORT")) + + // helper.OpenBrowser("http://localhost:" + helper.EnvGet("MCRM__PORT")) app.MCRMLog(http.ListenAndServe(":"+helper.EnvGet("MCRM__PORT"), nil))