diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..04f30f0 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/thisloke/wol-delemaco + +go 1.20 diff --git a/handlers.go b/handlers.go new file mode 100644 index 0000000..0c4333e --- /dev/null +++ b/handlers.go @@ -0,0 +1,216 @@ +package main + +import ( + "log" + "net/http" + "runtime" +) + +// Handle the root route - show status +func indexHandler(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/" { + http.NotFound(w, r) + return + } + + online := isServerOnline() + status := "Online" + color := "#4caf50" // Material green + if !online { + status = "Offline" + color = "#d32f2f" // Material red + } + + data := StatusData{ + Server: serverName, + Status: status, + Color: color, + IsTestMode: runtime.GOOS == "darwin", + AskPassword: false, + ErrorMessage: "", + } + + if err := tmpl.Execute(w, data); err != nil { + http.Error(w, "Failed to render template", http.StatusInternalServerError) + log.Printf("Template render error: %v", err) + } +} + +// Handle boot request +func bootHandler(w http.ResponseWriter, r *http.Request) { + if !isServerOnline() { + // Boot the server using wakeonlan + err := sendWakeOnLAN() + if err != nil { + log.Printf("Error booting server: %v", err) + } + + // Display booting status + data := StatusData{ + Server: serverName, + Status: "Booting", + Color: "#607d8b", // Material blue-gray + IsTestMode: runtime.GOOS == "darwin", + AskPassword: false, + } + if err := tmpl.Execute(w, data); err != nil { + http.Error(w, "Failed to render template", http.StatusInternalServerError) + log.Printf("Template render error: %v", err) + } + } else { + // Server is already online + data := StatusData{ + Server: serverName, + Status: "Online", + Color: "#4caf50", // Material green + IsTestMode: runtime.GOOS == "darwin", + AskPassword: false, + } + if err := tmpl.Execute(w, data); err != nil { + http.Error(w, "Failed to render template", http.StatusInternalServerError) + log.Printf("Template render error: %v", err) + } + } +} + +// Handle shutdown confirmation request +func confirmShutdownHandler(w http.ResponseWriter, r *http.Request) { + online := isServerOnline() + + if !online { + // Server is already offline + data := StatusData{ + Server: serverName, + Status: "Offline", + Color: "#d32f2f", // Material red + IsTestMode: runtime.GOOS == "darwin", + AskPassword: false, + } + if err := tmpl.Execute(w, data); err != nil { + http.Error(w, "Failed to render template", http.StatusInternalServerError) + log.Printf("Template render error: %v", err) + } + return + } + + // Show confirmation dialog + data := StatusData{ + Server: serverName, + Status: "Online", + Color: "#4caf50", // Material green + IsTestMode: runtime.GOOS == "darwin", + ConfirmShutdown: true, + AskPassword: false, + } + if err := tmpl.Execute(w, data); err != nil { + http.Error(w, "Failed to render template", http.StatusInternalServerError) + log.Printf("Template render error: %v", err) + } +} + +// Handle password entry for shutdown +func enterPasswordHandler(w http.ResponseWriter, r *http.Request) { + if !isServerOnline() { + // Server is already offline, redirect to home + http.Redirect(w, r, "/", http.StatusSeeOther) + return + } + + // Show password entry dialog + data := StatusData{ + Server: serverName, + Status: "Online", + Color: "#4caf50", // Material green + IsTestMode: runtime.GOOS == "darwin", + AskPassword: true, + } + if err := tmpl.Execute(w, data); err != nil { + http.Error(w, "Failed to render template", http.StatusInternalServerError) + log.Printf("Template render error: %v", err) + } +} + +// Handle actual shutdown request +func shutdownHandler(w http.ResponseWriter, r *http.Request) { + // Only process POST requests for security + if r.Method != "POST" { + http.Redirect(w, r, "/", http.StatusSeeOther) + return + } + + // Parse form data to get password + if err := r.ParseForm(); err != nil { + log.Printf("Error parsing form: %v", err) + http.Redirect(w, r, "/", http.StatusSeeOther) + return + } + + // Get password from form + password := r.FormValue("password") + + if password == "" { + // Show password form again with error + data := StatusData{ + Server: serverName, + Status: "Online", + Color: "#4caf50", + IsTestMode: runtime.GOOS == "darwin", + AskPassword: true, + ErrorMessage: "Password cannot be empty", + } + if err := tmpl.Execute(w, data); err != nil { + http.Error(w, "Failed to render template", http.StatusInternalServerError) + log.Printf("Template render error: %v", err) + } + return + } + + if isServerOnline() { + // Shutdown the server + err := shutdownServer(password) + if err != nil { + log.Printf("Error shutting down server: %v", err) + + // Show password form again with error + data := StatusData{ + Server: serverName, + Status: "Online", + Color: "#4caf50", + IsTestMode: runtime.GOOS == "darwin", + AskPassword: true, + ErrorMessage: "Failed to shutdown server. Please check your password.", + } + if err := tmpl.Execute(w, data); err != nil { + http.Error(w, "Failed to render template", http.StatusInternalServerError) + log.Printf("Template render error: %v", err) + } + return + } + + // Display shutting down status + data := StatusData{ + Server: serverName, + Status: "Shutting down", + Color: "#5d4037", // Material brown + IsTestMode: runtime.GOOS == "darwin", + AskPassword: false, + } + if err := tmpl.Execute(w, data); err != nil { + http.Error(w, "Failed to render template", http.StatusInternalServerError) + log.Printf("Template render error: %v", err) + } + } else { + // Server is already offline + data := StatusData{ + Server: serverName, + Status: "Offline", + Color: "#d32f2f", // Material red + IsTestMode: runtime.GOOS == "darwin", + AskPassword: false, + } + if err := tmpl.Execute(w, data); err != nil { + http.Error(w, "Failed to render template", http.StatusInternalServerError) + log.Printf("Template render error: %v", err) + } + } +} diff --git a/main.go b/main.go index affeb98..dac22ec 100644 --- a/main.go +++ b/main.go @@ -2,405 +2,29 @@ package main import ( "fmt" - "html/template" "log" "net/http" - "os/exec" "runtime" ) const ( serverName = "delemaco" // Server to ping + serverUser = "root" // SSH username macAddress = "b8:cb:29:a1:f3:88" // MAC address of the server port = "8080" // Port to listen on ) -// StatusData holds data for the HTML template -type StatusData struct { - Server string - Status string - Color string - IsTestMode bool -} - -// Check if server is online -func isServerOnline() bool { - var cmd *exec.Cmd - - // macOS and Linux have slightly different ping commands - if runtime.GOOS == "darwin" { - cmd = exec.Command("ping", "-c", "1", "-W", "1000", serverName) - } else { - cmd = exec.Command("ping", "-c", "1", "-W", "1", serverName) - } - - err := cmd.Run() - return err == nil -} - -// Send WOL packet -func sendWakeOnLAN() error { - log.Printf("Sending WOL packet to %s (%s)", serverName, macAddress) - cmd := exec.Command("wakeonlan", macAddress) - return cmd.Run() -} - -// Shutdown server -func shutdownServer() error { - - // Real shutdown command for Linux - log.Printf("Sending shutdown command to %s", serverName) - cmd := exec.Command("ssh", serverName, "sudo", "shutdown", "-h", "now") - return cmd.Run() -} - -// Handle the root route - show status -func indexHandler(w http.ResponseWriter, r *http.Request) { - if r.URL.Path != "/" { - http.NotFound(w, r) - return - } - - online := isServerOnline() - status := "Online" - color := "#4caf50" // Material green - if !online { - status = "Offline" - color = "#d32f2f" // Material red - } - - data := StatusData{ - Server: serverName, - Status: status, - Color: color, - IsTestMode: runtime.GOOS == "darwin", - } - - renderTemplate(w, data) -} - -// Handle boot request -func bootHandler(w http.ResponseWriter, r *http.Request) { - if !isServerOnline() { - // Boot the server using wakeonlan - err := sendWakeOnLAN() - if err != nil { - log.Printf("Error booting server: %v", err) - } - - // Display booting status - data := StatusData{ - Server: serverName, - Status: "Booting", - Color: "#607d8b", // Material blue-gray - IsTestMode: runtime.GOOS == "darwin", - } - renderTemplate(w, data) - } else { - // Server is already online - data := StatusData{ - Server: serverName, - Status: "Online", - Color: "#4caf50", // Material green - IsTestMode: runtime.GOOS == "darwin", - } - renderTemplate(w, data) - } -} - -// Handle shutdown request -func shutdownHandler(w http.ResponseWriter, r *http.Request) { - if isServerOnline() { - // Shutdown the server - err := shutdownServer() - if err != nil { - log.Printf("Error shutting down server: %v", err) - } - - // Display shutting down status - data := StatusData{ - Server: serverName, - Status: "Shutting down", - Color: "#5d4037", // Material brown - IsTestMode: runtime.GOOS == "darwin", - } - renderTemplate(w, data) - } else { - // Server is already offline - data := StatusData{ - Server: serverName, - Status: "Offline", - Color: "#d32f2f", // Material red - IsTestMode: runtime.GOOS == "darwin", - } - renderTemplate(w, data) - } -} - -// Render the HTML template -func renderTemplate(w http.ResponseWriter, data StatusData) { - tmpl, err := template.New("status").Parse(` - -
- - -