diff --git a/backend/db.go b/backend/db.go index e6348fa..ef3acd0 100644 --- a/backend/db.go +++ b/backend/db.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path/filepath" + "strings" "time" _ "github.com/mattn/go-sqlite3" @@ -43,17 +44,17 @@ func NewDB() (*DB, error) { func (db *DB) InitSchema(logger *Logger) error { // Drop tables if they exist // logger.Debug("Dropping tables if they exist...") - // _, err := db.Exec(`DROP TABLE IF EXISTS configs`) - // if err != nil { - // return err + // _, dropErr := db.Exec(`DROP TABLE IF EXISTS configs`) + // if dropErr != nil { + // return dropErr // } - // _, err = db.Exec(`DROP TABLE IF EXISTS script_configs`) - // if err != nil { - // return err + // _, dropErr = db.Exec(`DROP TABLE IF EXISTS script_configs`) + // if dropErr != nil { + // return dropErr // } - // _, err = db.Exec(`DROP TABLE IF EXISTS sensor_configs`) - // if err != nil { - // return err + // _, dropErr = db.Exec(`DROP TABLE IF EXISTS sensor_configs`) + // if dropErr != nil { + // return dropErr // } // Create tables @@ -109,17 +110,57 @@ func (db *DB) InitSchema(logger *Logger) error { INSERT INTO configs (id, broker_address, username, password, client_id, topic, log_level, script_timeout, created_at, updated_at) VALUES (1, 'tcp://0.0.0.0:1883', 'your_username', 'your_password', 'my-windows-automation-service', 'windows/commands', 'debug', 300, '2023-07-01 12:00:00', '2023-07-01 12:00:00'); - INSERT INTO script_configs (id, name, script_path, script_timeout, run_as_user, created_at, updated_at) - VALUES (1, 'test_notification', 'test_notification.ps1', 300, true, '2023-07-01 12:00:00', '2023-07-01 12:00:00'); - INSERT INTO sensor_configs (id, name, enabled, interval, sensor_topic, created_at, updated_at) VALUES (1, 'cpu_usage', false, 60, 'windows/sensors/cpu_usage', '2023-07-01 12:00:00', '2023-07-01 12:00:00'); `) + if err != nil { + return err + } } - + err = db.AddScriptsFromDir() return err } +func (db *DB) AddScriptsFromDir() error { + exePath, err := os.Executable() + if err != nil { + return fmt.Errorf("failed to get executable path: %v", err) + } + dir := filepath.Join(filepath.Dir(exePath), "scripts") + + files, err := os.ReadDir(dir) + if err != nil { + return fmt.Errorf("failed to read directory: %v", err) + } + + for _, file := range files { + if file.IsDir() { + continue + } + + // Check if db has script with same script path + var script ScriptConfig + err := db.QueryRow("SELECT id, name, script_path, run_as_user, script_timeout, created_at, updated_at FROM script_configs WHERE script_path = ?", file.Name()).Scan(&script) + // split file name into name and extension + filename := strings.Split(file.Name(), ".")[0] + + if err == sql.ErrNoRows { + // If not, add it + script := ScriptConfig{ + Name: filename, + ScriptPath: file.Name(), + RunAsUser: true, + ScriptTimeout: 300, + } + err = db.CreateScriptConfig(&script) + if err != nil { + return fmt.Errorf("failed to create script config: %v", err) + } + } + } + return nil +} + func (db *DB) GetConfig() (*Config, error) { var configModel ConfigModel @@ -145,7 +186,7 @@ func (db *DB) GetConfig() (*Config, error) { } configsScriptArray := make(map[string]ScriptConfig) for _, config := range *configsScript { - configsScriptArray[config.ScriptPath] = config + configsScriptArray[config.Name] = config } configsSensor, err := db.GetSensorConfigs() @@ -288,3 +329,19 @@ func (db *DB) CreateSensorConfig(sensorConfig *SensorConfig) error { ) return err } + +func (db *DB) CreateScriptConfig(scriptConf *ScriptConfig) error { + now := time.Now() + _, err := db.Exec(` + INSERT INTO script_configs ( + name, script_path, run_as_user, script_timeout, created_at, updated_at + ) VALUES (?, ?, ?, ?, ?, ?)`, + scriptConf.Name, + scriptConf.ScriptPath, + scriptConf.RunAsUser, + scriptConf.ScriptTimeout, + now, + now, + ) + return err +} diff --git a/backend/http_server.go b/backend/http_server.go index 6463f85..32ce3a5 100644 --- a/backend/http_server.go +++ b/backend/http_server.go @@ -25,6 +25,7 @@ func (p *program) startHTTPServer() { r.HandleFunc("/api/config", p.handleUpdateConfig).Methods("POST") r.HandleFunc("/api/scripts", p.handleListScripts).Methods("GET") r.HandleFunc("/api/scripts", p.handleAddScript).Methods("POST") + r.HandleFunc("/api/restart", p.handleRestartService).Methods("POST") // Serve static files (our UI) - this will be added at build time from our Nuxt frontend r.PathPrefix("/").Handler(http.FileServer(http.Dir(staticPath))) @@ -81,3 +82,14 @@ func (p *program) handleAddScript(w http.ResponseWriter, r *http.Request) { p.logger.Debug("Handling /api/scripts POST request") // Logic to add new powershell scripts } + +func (p *program) handleRestartService(w http.ResponseWriter, r *http.Request) { + p.logger.Debug("Handling /api/restart POST request") + err := p.restartService() + if err != nil { + p.logger.Error(fmt.Sprintf("Failed to restart service: %v", err)) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + w.WriteHeader(http.StatusOK) +} diff --git a/backend/service.go b/backend/service.go index 51f107b..c4da773 100644 --- a/backend/service.go +++ b/backend/service.go @@ -167,3 +167,20 @@ func (p *program) executeScript(scriptPath string, runAsUser bool) (string, erro return p.runAsLocalSystem(scriptPath) } } + +func (p *program) restartService() error { + p.logger.Debug("Restarting service") + err := p.Stop(nil) + if err != nil { + return fmt.Errorf("failed to stop service: %v", err) + } + // clear mqtt client + p.mqttClient = nil + time.Sleep(time.Second * 5) + p.logger.Debug("Service stopped, restarting...") + err = p.Start(nil) + if err != nil { + return fmt.Errorf("failed to start service: %v", err) + } + return nil +} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 33e23f8..a4ed710 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,16 +1,17 @@ { - "name": "nuxt-app", + "name": "mqtt-win-control", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "nuxt-app", + "name": "mqtt-win-control", "hasInstallScript": true, "dependencies": { "@nuxtjs/tailwindcss": "^6.12.1", "nuxt": "^3.13.0", "vue": "latest", - "vue-router": "latest" + "vue-router": "latest", + "vue3-toastify": "^0.2.2" } }, "node_modules/@alloc/quick-lru": { @@ -10725,6 +10726,28 @@ "vue": "^3.2.0" } }, + "node_modules/vue3-toastify": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/vue3-toastify/-/vue3-toastify-0.2.2.tgz", + "integrity": "sha512-D8pmIp2UeU8MU1OY7GktA70HviZ38b1RagN82P7tFu3abUD86w+PjfmbdRch4QVtjVxK+eqKLvi5cXJRndwJfw==", + "license": "MIT", + "workspaces": [ + "docs", + "playground" + ], + "engines": { + "node": ">=18.18.0", + "npm": ">=9.0.0" + }, + "peerDependencies": { + "vue": ">=3.2.0" + }, + "peerDependenciesMeta": { + "vue": { + "optional": true + } + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 27b1250..f3368e5 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,6 +13,7 @@ "@nuxtjs/tailwindcss": "^6.12.1", "nuxt": "^3.13.0", "vue": "latest", - "vue-router": "latest" + "vue-router": "latest", + "vue3-toastify": "^0.2.2" } } diff --git a/frontend/pages/config/mqtt.vue b/frontend/pages/config/mqtt.vue index 9b26e2f..a3511b6 100644 --- a/frontend/pages/config/mqtt.vue +++ b/frontend/pages/config/mqtt.vue @@ -1,5 +1,5 @@ \ No newline at end of file diff --git a/frontend/plugins/toast.client.js b/frontend/plugins/toast.client.js new file mode 100644 index 0000000..cc0bc6f --- /dev/null +++ b/frontend/plugins/toast.client.js @@ -0,0 +1,10 @@ +import Vue3Toastify, { toast } from 'vue3-toastify'; +import 'vue3-toastify/dist/index.css'; + +export default defineNuxtPlugin((nuxtApp) => { + nuxtApp.vueApp.use(Vue3Toastify, { autoClose: 2000 }); + + return { + provide: { toast }, + }; +}); \ No newline at end of file