bugfixing
This commit is contained in:
parent
6ea4d31109
commit
2056479224
105
main.go
105
main.go
|
@ -2,22 +2,23 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"fmt"
|
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"gontrol/src/randomname"
|
|
||||||
"gontrol/src/server/database"
|
|
||||||
"gontrol/src/logger"
|
"gontrol/src/logger"
|
||||||
|
"gontrol/src/randomname"
|
||||||
api "gontrol/src/server/api"
|
api "gontrol/src/server/api"
|
||||||
|
"gontrol/src/server/database"
|
||||||
websocketserver "gontrol/src/server/websocket"
|
websocketserver "gontrol/src/server/websocket"
|
||||||
|
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
@ -31,6 +32,8 @@ var (
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Database struct {
|
Database struct {
|
||||||
Username string `envconfig:"DB_USERNAME"`
|
Username string `envconfig:"DB_USERNAME"`
|
||||||
|
@ -55,22 +58,14 @@ func processError(err error) {
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
tmpl, _ = template.ParseGlob("templates/*.html")
|
tmpl, _ = template.ParseGlob("templates/*.html")
|
||||||
|
|
||||||
|
// Sqlite3
|
||||||
|
err := logger.InitDB("/tmp/gontrol_logs.db")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// func initDB (dbUser string, dbPassword string, dbHost string, dbPort int16, dbName string ) {
|
|
||||||
// var err error
|
|
||||||
// dbOpen := fmt.Sprintf("%s:%s@(%s:%d)/%s?parseTime=true", dbUser,dbPassword, dbHost, dbPort, dbName)
|
|
||||||
// db, err = sql.Open("mysql", dbOpen)
|
|
||||||
// if err != nil {
|
|
||||||
// log.Fatal(err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if err = db.Ping(); err != nil {
|
|
||||||
// log.Fatal(err)
|
|
||||||
// }
|
|
||||||
// log.Println("Connected to database")
|
|
||||||
// }
|
|
||||||
|
|
||||||
func renderTemplate(w http.ResponseWriter, tmpl string, data interface{}) {
|
func renderTemplate(w http.ResponseWriter, tmpl string, data interface{}) {
|
||||||
t, err := template.ParseFiles(tmpl)
|
t, err := template.ParseFiles(tmpl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -119,7 +114,24 @@ func getHomepage(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func listAgents(w http.ResponseWriter, r *http.Request) {
|
func listAgents(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var agents []api.Agent
|
||||||
agents, err := api.GetAgents(db)
|
agents, err := api.GetAgents(db)
|
||||||
|
currentAgents := getAgentsStatus()
|
||||||
|
|
||||||
|
for _, currAgent := range currentAgents {
|
||||||
|
for i, agent := range agents {
|
||||||
|
if currAgent == agent.AgentName {
|
||||||
|
// log.Printf("%s online", agent.AgentName)
|
||||||
|
// logger.InsertLog(logger.Debug, fmt.Sprintf("%s online after page refresh", agent.AgentName))
|
||||||
|
// agents[i].Status = fmt.Sprint("<span class=\"badge bg-success\">Connected</span>")
|
||||||
|
agents[i].Status = "Connected"
|
||||||
|
} else {
|
||||||
|
// agent.Status = fmt.Sprintf("<span class=\"badge bg-danger\">Disconnected</span>")
|
||||||
|
agents[i].Status = "Disconnected"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "Failed to fetch agents", http.StatusInternalServerError)
|
http.Error(w, "Failed to fetch agents", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
@ -128,6 +140,23 @@ func listAgents(w http.ResponseWriter, r *http.Request) {
|
||||||
renderTemplate(w, "templates/partials/agent_list.html", agents)
|
renderTemplate(w, "templates/partials/agent_list.html", agents)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getAgentsStatus() []string {
|
||||||
|
resp, err := http.Get("http://localhost:5555/agentNames")
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error fetching agent names:", err)
|
||||||
|
logger.InsertLog(logger.Error, "Error fetching agent names from websocketServer")
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var agentNames []string
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&agentNames); err != nil {
|
||||||
|
log.Println("Error decoding response:", err)
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return agentNames
|
||||||
|
}
|
||||||
|
|
||||||
func getAgentNames(w http.ResponseWriter, r *http.Request) {
|
func getAgentNames(w http.ResponseWriter, r *http.Request) {
|
||||||
api.GetAgentNames(db, w, r)
|
api.GetAgentNames(db, w, r)
|
||||||
return
|
return
|
||||||
|
@ -139,25 +168,38 @@ func getAgentIds(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func logsHandler(w http.ResponseWriter, r *http.Request) {
|
func logsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var logs[] logger.LogEntry
|
||||||
logs, err := logger.FetchLogs(10)
|
logs, err := logger.FetchLogs(10)
|
||||||
|
|
||||||
|
// for i, logLine := range logs {
|
||||||
|
// logs[i].Message = strings.ReplaceAll(logLine.Message, `"`, `\"`)
|
||||||
|
// }
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "Error fetching logs", http.StatusInternalServerError)
|
http.Error(w, "Error fetching logs", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(w, "<div>")
|
|
||||||
// for _, logEntry := range logsToSend {
|
|
||||||
for _, logEntry := range logs {
|
|
||||||
fmt.Fprintf(w, "<p><strong>[%s] [%s]</strong> %s</p>", logEntry.Timestamp, logEntry.Level, logEntry.Message)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "</div>")
|
|
||||||
|
|
||||||
|
renderTemplate(w, "templates/partials/logs_partial.html", logs)
|
||||||
|
|
||||||
|
// fmt.Fprintf(w, "<div>")
|
||||||
|
// // for _, logEntry := range logsToSend {
|
||||||
|
// for _, logEntry := range logs {
|
||||||
|
// fmt.Fprintf(w, "<p><strong>[%s] [%s]</strong> %s</p>", logEntry.Timestamp, logEntry.Level, logEntry.Message)
|
||||||
|
// }
|
||||||
|
// fmt.Fprintf(w, "</div>")
|
||||||
|
|
||||||
|
// w.Header().Set("Content-Type", "application/json")
|
||||||
|
// json.NewEncoder(w).Encode(logs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
|
// sqlite3 has been initialized in init()
|
||||||
|
defer logger.CloseDB()
|
||||||
|
|
||||||
var cfg Config
|
var cfg Config
|
||||||
readEnv(&cfg)
|
readEnv(&cfg)
|
||||||
|
|
||||||
|
@ -176,18 +218,11 @@ func main() {
|
||||||
webMux.HandleFunc("/agents/{agentId}", agentsHandler)
|
webMux.HandleFunc("/agents/{agentId}", agentsHandler)
|
||||||
webMux.HandleFunc("/logs", logsHandler)
|
webMux.HandleFunc("/logs", logsHandler)
|
||||||
|
|
||||||
// Sqlite3
|
|
||||||
err := logger.InitDB("/tmp/gontrol_logs.db")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer logger.CloseDB()
|
|
||||||
// initDB (cfg.Database.Username, cfg.Database.Password, cfg.Database.Host, cfg.Database.Port, cfg.Database.Name)
|
|
||||||
// mysql
|
|
||||||
db = database.InitDB (cfg.Database.Username, cfg.Database.Password, cfg.Database.Host, cfg.Database.Port, cfg.Database.Name)
|
db = database.InitDB (cfg.Database.Username, cfg.Database.Password, cfg.Database.Host, cfg.Database.Port, cfg.Database.Name)
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
name := randomname.GenerateRandomName()
|
name := randomname.GenerateRandomName()
|
||||||
fmt.Println(name)
|
fmt.Sprintf("Server instance: %s", name)
|
||||||
|
|
||||||
webServer := &http.Server {
|
webServer := &http.Server {
|
||||||
Addr: ":3333",
|
Addr: ":3333",
|
||||||
|
@ -202,7 +237,7 @@ func main() {
|
||||||
if err := websocketServer.ListenAndServe(); err != http.ErrServerClosed {
|
if err := websocketServer.ListenAndServe(); err != http.ErrServerClosed {
|
||||||
log.Fatalf("Websocket server failed: %s", err)
|
log.Fatalf("Websocket server failed: %s", err)
|
||||||
}
|
}
|
||||||
err = logger.InsertLog(logger.Info, logLine)
|
err := logger.InsertLog(logger.Info, logLine)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Error inserting log:", err)
|
log.Println("Error inserting log:", err)
|
||||||
}
|
}
|
||||||
|
@ -218,7 +253,7 @@ func main() {
|
||||||
log.Fatalf("Web server failed: %s", err)
|
log.Fatalf("Web server failed: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = logger.InsertLog(logger.Info, logLine)
|
err := logger.InsertLog(logger.Info, logLine)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Error inserting log:", err)
|
log.Println("Error inserting log:", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
// "net/http"
|
// "net/http"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -17,16 +18,16 @@ var (
|
||||||
lite_dbMutex sync.Mutex
|
lite_dbMutex sync.Mutex
|
||||||
|
|
||||||
logMutex sync.Mutex
|
logMutex sync.Mutex
|
||||||
logLimit = 10
|
logLimit = 100
|
||||||
// LogEntries []string
|
// LogEntries []string
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Debug LogLevel = "DEBUG"
|
Debug LogLevel = "debug"
|
||||||
Info LogLevel = "INFO"
|
Info LogLevel = "info"
|
||||||
Warning LogLevel = "WARNING"
|
Warning LogLevel = "warning"
|
||||||
Error LogLevel = "ERROR"
|
Error LogLevel = "error"
|
||||||
Fatal LogLevel = "FATAL"
|
Fatal LogLevel = "fatal"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LogLevel string
|
type LogLevel string
|
||||||
|
@ -74,6 +75,8 @@ func InsertLog(level LogLevel, message string) error {
|
||||||
return fmt.Errorf("Error starting transaction: %w", err)
|
return fmt.Errorf("Error starting transaction: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message = strings.ReplaceAll(message, `"`, `\"`)
|
||||||
|
|
||||||
// insertQuery := `INSERT INTO logs (message) VALUES (?)`
|
// insertQuery := `INSERT INTO logs (message) VALUES (?)`
|
||||||
insertQuery := `INSERT INTO logs (message, level) VALUES (?, ?)`
|
insertQuery := `INSERT INTO logs (message, level) VALUES (?, ?)`
|
||||||
// _, err := Lite_db.Exec(insertQuery, message, level)
|
// _, err := Lite_db.Exec(insertQuery, message, level)
|
||||||
|
|
|
@ -60,21 +60,22 @@ func registerAgent(agentName, agentId, agentIp, agentType string) error {
|
||||||
resp, err := http.PostForm(registerURL, form)
|
resp, err := http.PostForm(registerURL, form)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error registering agent: %v", err)
|
log.Printf("Error registering agent: %v", err)
|
||||||
|
logger.InsertLog(logger.Error, fmt.Sprintf("Error registering agent: %v", err))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode == http.StatusCreated {
|
if resp.StatusCode == http.StatusCreated {
|
||||||
logLine := fmt.Sprintf("Agent %s successfully registered.", agentName)
|
log.Printf("Agent %s successfully registered.", agentName)
|
||||||
log.Printf(logLine)
|
logger.InsertLog(logger.Info, fmt.Sprintf("Agent %s successfully registered.", agentName))
|
||||||
// logLine = logger.ToLog(logLine)
|
|
||||||
logger.InsertLog(logger.Info, logLine)
|
|
||||||
return nil
|
return nil
|
||||||
} else if resp.StatusCode == http.StatusOK {
|
} else if resp.StatusCode == http.StatusOK {
|
||||||
log.Printf("Agent %s already registered.", agentName)
|
log.Printf("Agent %s already registered.", agentName)
|
||||||
|
logger.InsertLog(logger.Info, fmt.Sprintf("Agent %s already registered.", agentName))
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
log.Printf("Failed to register agent, status: %v", resp.Status)
|
log.Printf("Failed to register agent, status: %v", resp.Status)
|
||||||
|
logger.InsertLog(logger.Error, fmt.Sprintf("Failed to register agent, status: %v", resp.Status))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,6 +88,7 @@ func getAgentDetails(agentId string) (*api.Agent, error) {
|
||||||
resp, err := http.Get(agentURL)
|
resp, err := http.Get(agentURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to make GET request: %s", err)
|
log.Printf("Failed to make GET request: %s", err)
|
||||||
|
logger.InsertLog(logger.Error, fmt.Sprintf("Failed to make GET request: %s", err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
@ -94,6 +96,7 @@ func getAgentDetails(agentId string) (*api.Agent, error) {
|
||||||
doc, err := goquery.NewDocumentFromReader(resp.Body)
|
doc, err := goquery.NewDocumentFromReader(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to parse HTML: %s", err)
|
log.Printf("Failed to parse HTML: %s", err)
|
||||||
|
logger.InsertLog(logger.Error, fmt.Sprintf("Failed to parse HTML: %s", err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,6 +107,7 @@ func getAgentDetails(agentId string) (*api.Agent, error) {
|
||||||
agent.AgentID, err = strconv.Atoi(strings.TrimSpace(strings.TrimPrefix(text, "ID:")))
|
agent.AgentID, err = strconv.Atoi(strings.TrimSpace(strings.TrimPrefix(text, "ID:")))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Converting string to integer failed in getAgentDetails(): %s", err)
|
log.Printf("Converting string to integer failed in getAgentDetails(): %s", err)
|
||||||
|
logger.InsertLog(logger.Error, fmt.Sprintf("Converting string to integer failed in getAgentDetails(): %s", err))
|
||||||
}
|
}
|
||||||
} else if strings.HasPrefix(text, "Name:") {
|
} else if strings.HasPrefix(text, "Name:") {
|
||||||
agent.AgentName = strings.TrimSpace(strings.TrimPrefix(text, "Name:"))
|
agent.AgentName = strings.TrimSpace(strings.TrimPrefix(text, "Name:"))
|
||||||
|
@ -130,24 +134,28 @@ func getAgentIds() ([]string, error) {
|
||||||
resp, err := http.Get(idURL)
|
resp, err := http.Get(idURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to make GET request: %s", err)
|
log.Printf("Failed to make GET request: %s", err)
|
||||||
|
logger.InsertLog(logger.Error, fmt.Sprintf("Failed to make GET request: %s", err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
log.Printf("Unexpected status code: %d", resp.StatusCode)
|
log.Printf("Unexpected status code: %d", resp.StatusCode)
|
||||||
|
logger.InsertLog(logger.Info, fmt.Sprintf("Unexpected status code: %d", resp.StatusCode))
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to read response body: %s", err)
|
log.Printf("Failed to read response body: %s", err)
|
||||||
|
logger.InsertLog(logger.Error, fmt.Sprintf("Failed to read response body: %s", err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var agentIds []string
|
var agentIds []string
|
||||||
if err := json.Unmarshal(body, &agentIds); err != nil {
|
if err := json.Unmarshal(body, &agentIds); err != nil {
|
||||||
log.Printf("Failed to parse JSON response: %s", err)
|
log.Printf("Failed to parse JSON response: %s", err)
|
||||||
|
logger.InsertLog(logger.Error, fmt.Sprintf("Failed to parse JSON response: %s", err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,6 +167,7 @@ func (wsh webSocketHandler) ServeHTTP(w http.ResponseWriter, r *http.Request){
|
||||||
c, err := wsh.upgrader.Upgrade(w, r, nil)
|
c, err := wsh.upgrader.Upgrade(w, r, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error %s when upgrading connection to websocket", err)
|
log.Printf("Error %s when upgrading connection to websocket", err)
|
||||||
|
logger.InsertLog(logger.Error, fmt.Sprintf("Error %s when upgrading connection to websocket", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,13 +192,13 @@ func (wsh webSocketHandler) ServeHTTP(w http.ResponseWriter, r *http.Request){
|
||||||
|
|
||||||
if agentName == "" || agentIP == "" {
|
if agentName == "" || agentIP == "" {
|
||||||
log.Printf("Missing agentName or IPv4Address in query parameters")
|
log.Printf("Missing agentName or IPv4Address in query parameters")
|
||||||
|
logger.InsertLog(logger.Info, fmt.Sprintf("Missing agentName or IPv4Address in query parameters"))
|
||||||
c.Close()
|
c.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logLine := fmt.Sprintf("Agent %s connected: %s (%s)", agentId, agentName, agentIP)
|
log.Printf("Agent %s connected: %s (%s)", agentId, agentName, agentIP)
|
||||||
log.Printf(logLine)
|
logger.InsertLog(logger.Info, fmt.Sprintf("Agent %s connected: %s (%s)", agentId, agentName, agentIP))
|
||||||
logger.InsertLog(logger.Info, logLine)
|
|
||||||
|
|
||||||
agentSocketsMutex.Lock()
|
agentSocketsMutex.Lock()
|
||||||
agentSockets[agentName] = c
|
agentSockets[agentName] = c
|
||||||
|
@ -200,22 +209,19 @@ func (wsh webSocketHandler) ServeHTTP(w http.ResponseWriter, r *http.Request){
|
||||||
delete(agentSockets, agentName)
|
delete(agentSockets, agentName)
|
||||||
agentSocketsMutex.Unlock()
|
agentSocketsMutex.Unlock()
|
||||||
c.Close()
|
c.Close()
|
||||||
logLine := fmt.Sprintf("Agent disconnected: %s (%s)", agentName, agentIP)
|
log.Printf("Agent disconnected: %s (%s)", agentName, agentIP)
|
||||||
log.Printf(logLine)
|
logger.InsertLog(logger.Info, fmt.Sprintf("Agent disconnected: %s (%s)", agentName, agentIP))
|
||||||
logger.InsertLog(logger.Info, logLine)
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
_, message, err := c.ReadMessage()
|
_, message, err := c.ReadMessage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logLine := fmt.Sprintf("Error reading from agent %s: %v", agentName, err)
|
log.Printf("Error reading from agent %s: %v", agentName, err)
|
||||||
log.Printf(logLine)
|
logger.InsertLog(logger.Error, fmt.Sprintf("Error reading from agent %s: %v", agentName, err))
|
||||||
logger.InsertLog(logger.Error, logLine)
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
logLine := fmt.Sprintf("Message from agent %s: %s", agentName, message)
|
log.Printf("Message from agent %s: %s", agentName, message)
|
||||||
log.Printf(logLine)
|
logger.InsertLog(logger.Info, fmt.Sprintf("Message from agent %s: %s", agentName, message))
|
||||||
logger.InsertLog(logger.Debug, logLine)
|
|
||||||
|
|
||||||
if ch, ok := responseChannels.Load(agentName); ok {
|
if ch, ok := responseChannels.Load(agentName); ok {
|
||||||
responseChan := ch.(chan string)
|
responseChan := ch.(chan string)
|
||||||
|
@ -233,6 +239,7 @@ var executeCommand http.HandlerFunc = func(w http.ResponseWriter, r *http.Reques
|
||||||
err := r.ParseForm()
|
err := r.ParseForm()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "Invalid form data", http.StatusBadRequest)
|
http.Error(w, "Invalid form data", http.StatusBadRequest)
|
||||||
|
logger.InsertLog(logger.Info, "Invalid form data")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,6 +252,7 @@ var executeCommand http.HandlerFunc = func(w http.ResponseWriter, r *http.Reques
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
http.Error(w, "Agent not connected", http.StatusNotFound)
|
http.Error(w, "Agent not connected", http.StatusNotFound)
|
||||||
|
logger.InsertLog(logger.Info, "Agent not connected")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,6 +270,7 @@ var executeCommand http.HandlerFunc = func(w http.ResponseWriter, r *http.Reques
|
||||||
err = conn.WriteMessage(websocket.TextMessage, messageBytes)
|
err = conn.WriteMessage(websocket.TextMessage, messageBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "Failed to send command to the agent", http.StatusInternalServerError)
|
http.Error(w, "Failed to send command to the agent", http.StatusInternalServerError)
|
||||||
|
logger.InsertLog(logger.Error, "Failed to send command to the agent")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,13 +284,15 @@ var executeCommand http.HandlerFunc = func(w http.ResponseWriter, r *http.Reques
|
||||||
payload, ok := parsedResponse["payload"]
|
payload, ok := parsedResponse["payload"]
|
||||||
if !ok {
|
if !ok {
|
||||||
http.Error(w, "Invalid response structure", http.StatusInternalServerError)
|
http.Error(w, "Invalid response structure", http.StatusInternalServerError)
|
||||||
|
logger.InsertLog(logger.Error, "Invalid response structure")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
w.Header().Set("Content-Type", "text/plain")
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
w.Write([]byte(payload))
|
w.Write([]byte(payload))
|
||||||
case <- time.After(10 * time.Second):
|
case <- time.After(10 * time.Second):
|
||||||
http.Error(w, "Agent repsonse timed out", http.StatusGatewayTimeout)
|
http.Error(w, "Agent response timed out", http.StatusGatewayTimeout)
|
||||||
|
logger.InsertLog(logger.Info, "Agent response timed out")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,45 +11,119 @@
|
||||||
<title>g2: gommand & gontrol</title>
|
<title>g2: gommand & gontrol</title>
|
||||||
<script>
|
<script>
|
||||||
// Query Agents for the Dropdown Menu
|
// Query Agents for the Dropdown Menu
|
||||||
|
<!-- document.addEventListener('DOMContentLoaded', () => { -->
|
||||||
|
<!-- fetch('/agentNames') -->
|
||||||
|
<!-- .then(response => response.json()) -->
|
||||||
|
<!-- .then(agentNames => { -->
|
||||||
|
<!-- const dropdown = document.getElementById('agentName'); -->
|
||||||
|
<!-- agentNames.forEach(name => { -->
|
||||||
|
<!-- const option = document.createElement('option'); -->
|
||||||
|
<!-- option.value = name; -->
|
||||||
|
<!-- option.textContent = name; -->
|
||||||
|
<!-- dropdown.appendChild(option); -->
|
||||||
|
<!-- }); -->
|
||||||
|
<!-- }) -->
|
||||||
|
<!-- .catch(error => console.error('Error fetching agent names:', error)); -->
|
||||||
|
<!-- }); -->
|
||||||
|
<!-- // Query agents currently connected to the websocket and put status into the table -->
|
||||||
|
<!-- const updateAgentStatuses = () => { -->
|
||||||
|
<!-- fetch('http://localhost:5555/agentNames') -->
|
||||||
|
<!-- .then(response => response.json()) -->
|
||||||
|
<!-- .then(agentNames => { -->
|
||||||
|
<!-- console.log("Agent names fetched:", agentNames); -->
|
||||||
|
<!-- const tableRows = document.querySelectorAll('#agentList table tbody tr'); -->
|
||||||
|
<!-- tableRows.forEach(row => { -->
|
||||||
|
<!-- const nameCell = row.querySelector('td:nth-child(2)'); -->
|
||||||
|
<!-- const statusCell = row.querySelector('td:nth-child(5)'); -->
|
||||||
|
<!-- if (nameCell && statusCell) { -->
|
||||||
|
<!-- const agentName = nameCell.textContent.trim(); -->
|
||||||
|
<!-- if (agentNames.includes(agentName)) { -->
|
||||||
|
<!-- statusCell.innerHTML = '<span class="badge bg-success">Connected</span>'; -->
|
||||||
|
<!-- } else { -->
|
||||||
|
<!-- statusCell.innerHTML = '<span class="badge bg-danger">Disconnected</span>'; -->
|
||||||
|
<!-- } -->
|
||||||
|
<!-- } -->
|
||||||
|
<!-- }); -->
|
||||||
|
<!-- }) -->
|
||||||
|
<!-- .catch(error => console.error('Error fetching agent names:', error)); -->
|
||||||
|
<!-- }; -->
|
||||||
|
<!-- updateAgentStatuses(); -->
|
||||||
|
<!-- setInterval(updateAgentStatuses, 5000); -->
|
||||||
|
|
||||||
|
<!-- document.body.addEventListener('htmx:afterSwap', function(evt) { -->
|
||||||
|
<!-- if (evt.detail.xhr.status === 200) { -->
|
||||||
|
<!-- const tableRows = document.querySelectorAll('#agentList table tbody tr'); -->
|
||||||
|
<!-- tableRows.forEach(row => { -->
|
||||||
|
<!-- const nameCell = row.querySelector('td:nth-child(2)'); -->
|
||||||
|
<!-- const statusCell = row.querySelector('td:nth-child(5)'); -->
|
||||||
|
<!-- if (nameCell && statusCell) { -->
|
||||||
|
<!-- const agentName = nameCell.textContent.trim(); -->
|
||||||
|
<!-- if ("Connected" === statusCell.innerHTML) { -->
|
||||||
|
<!-- statusCell.innerHTML = '<span class="badge bg-success">Connected</span>'; -->
|
||||||
|
<!-- } else { -->
|
||||||
|
<!-- statusCell.innerHTML = '<span class="badge bg-danger">Disconnected</span>'; -->
|
||||||
|
<!-- } -->
|
||||||
|
<!-- } -->
|
||||||
|
<!-- } -->
|
||||||
|
<!-- } -->
|
||||||
|
<!-- }); -->
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
fetch('/agentNames')
|
document.body.addEventListener('htmx:afterSwap', function(event) {
|
||||||
.then(response => response.json())
|
if (event.target.id === "agentList") {
|
||||||
.then(agentNames => {
|
updateAgentDropdown();
|
||||||
const dropdown = document.getElementById('agentName');
|
}
|
||||||
agentNames.forEach(name => {
|
});
|
||||||
const option = document.createElement('option');
|
|
||||||
|
function updateAgentDropdown() {
|
||||||
|
const select = document.getElementById("agentName");
|
||||||
|
const optionValues = Array.from(select.options).map(opt => opt.value);
|
||||||
|
const rows = document.querySelectorAll("#agentList tbody tr");
|
||||||
|
|
||||||
|
rows.forEach(row => {
|
||||||
|
const status = row.cells[4].textContent.trim();
|
||||||
|
const name = row.cells[1].textContent.trim();
|
||||||
|
|
||||||
|
if (status === "Connected") {
|
||||||
|
row.cells[4].innerHTML = '<span class="badge bg-success">Connected</span>';
|
||||||
|
const option = document.createElement("option");
|
||||||
|
if (!(optionValues.includes(name))) {
|
||||||
option.value = name;
|
option.value = name;
|
||||||
option.textContent = name;
|
option.textContent = name;
|
||||||
dropdown.appendChild(option);
|
select.appendChild(option);
|
||||||
});
|
}
|
||||||
})
|
}
|
||||||
.catch(error => console.error('Error fetching agent names:', error));
|
|
||||||
});
|
if (status === "Disconnected") {
|
||||||
// Query agents currently connected to the websocket and put status into the table
|
row.cells[4].innerHTML = '<span class="badge bg-danger">Disconnected</span>';
|
||||||
const updateAgentStatuses = () => {
|
const option = Array.from(select.options).find(opt => opt.value === name);
|
||||||
fetch('http://localhost:5555/agentNames')
|
if(option) {
|
||||||
.then(response => response.json())
|
select.removeChild(option);
|
||||||
.then(agentNames => {
|
|
||||||
console.log("Agent names fetched:", agentNames);
|
|
||||||
const tableRows = document.querySelectorAll('#agentList table tbody tr');
|
|
||||||
tableRows.forEach(row => {
|
|
||||||
const nameCell = row.querySelector('td:nth-child(2)');
|
|
||||||
const statusCell = row.querySelector('td:nth-child(5)');
|
|
||||||
if (nameCell && statusCell) {
|
|
||||||
const agentName = nameCell.textContent.trim();
|
|
||||||
if (agentNames.includes(agentName)) {
|
|
||||||
statusCell.innerHTML = '<span class="badge bg-success">Connected</span>';
|
|
||||||
} else {
|
|
||||||
statusCell.innerHTML = '<span class="badge bg-danger">Disconnected</span>';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
}
|
||||||
.catch(error => console.error('Error fetching agent names:', error));
|
});
|
||||||
};
|
|
||||||
updateAgentStatuses();
|
|
||||||
setInterval(updateAgentStatuses, 1000);
|
|
||||||
</script>
|
</script>
|
||||||
|
<style>
|
||||||
|
.log-info {
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
.log-warning {
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
.log-error {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
.log-fatal {
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
.log-debug {
|
||||||
|
color: violet;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
@ -58,15 +132,17 @@
|
||||||
<h2>Agents</h2>
|
<h2>Agents</h2>
|
||||||
|
|
||||||
<!-- Agent List -->
|
<!-- Agent List -->
|
||||||
<!-- <div id="agentList" hx-get="/agents" hx-trigger="load, every 2s" hx-swap="innerHTML"></div> -->
|
<div id="agentList" hx-get="/agents" hx-trigger="load, every 2s" hx-swap="innerHTML"></div>
|
||||||
<div id="agentList" hx-get="/agents" hx-trigger="load" hx-swap="innerHTML"></div>
|
<!-- <div id="agentList" hx-get="/agents" hx-trigger="load" hx-swap="innerHTML"></div> -->
|
||||||
<!-- Agent Commands -->
|
<!-- Agent Commands -->
|
||||||
<div id="agentCommands">
|
<div id="agentCommands">
|
||||||
<h3>Command Execution</h3>
|
<h3>Command Execution</h3>
|
||||||
<form hx-post="http://localhost:5555/executeCommand" hx-target="#commandOutput" hx-encoding="application/x-www-form-urlencoded" hx-swap="innerHTML">
|
<form hx-post="http://localhost:5555/executeCommand" hx-target="#commandOutput" hx-encoding="application/x-www-form-urlencoded" hx-swap="innerHTML">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="agentName" class="form-label">Agent Name</label>
|
<label for="agentName" class="form-label">Agent Name</label>
|
||||||
<select class="form-select" id="agentName" name="agentName" required>
|
<!-- <select class="form-select" id="agentName" name="agentName" required> -->
|
||||||
|
<select id="agentName" class="form-select" name="agentName" hx-on="htmx:afterSwap:updateAgentDropdown" required>
|
||||||
|
<option value="" disabled selected>Select an Agent</option>
|
||||||
<!-- Dynamically populated with agent names -->
|
<!-- Dynamically populated with agent names -->
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -80,33 +156,46 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Add Agent Form -->
|
<!-- Add Agent Form -->
|
||||||
<button class="btn btn-primary mt-3" data-bs-toggle="collapse" data-bs-target="#addAgentForm">Add Agent</button>
|
<!-- <button class="btn btn-primary mt-3" data-bs-toggle="collapse" data-bs-target="#addAgentForm">Add Agent</button> -->
|
||||||
<div id="addAgentForm" class="collapse mt-2">
|
<!-- <div id="addAgentForm" class="collapse mt-2"> -->
|
||||||
<form hx-post="/agents" hx-target="#agentList" hx-swap="innerHTML">
|
<!-- <form hx-post="/agents" hx-target="#agentList" hx-swap="innerHTML"> -->
|
||||||
<div class="mb-3">
|
<!-- <div class="mb-3"> -->
|
||||||
<label for="agentId" class="form-label">Agent Id</label>
|
<!-- <label for="agentId" class="form-label">Agent Id</label> -->
|
||||||
<input type="text" class="form-control" id="agentId" name="agentId" required>
|
<!-- <input type="text" class="form-control" id="agentId" name="agentId" required> -->
|
||||||
</div>
|
<!-- </div> -->
|
||||||
<div class="mb-3">
|
<!-- <div class="mb-3"> -->
|
||||||
<label for="agentName" class="form-label">Agent Name</label>
|
<!-- <label for="agentName" class="form-label">Agent Name</label> -->
|
||||||
<input type="text" class="form-control" id="agentName" name="agentName" required>
|
<!-- <input type="text" class="form-control" id="agentName" name="agentName" required> -->
|
||||||
</div>
|
<!-- </div> -->
|
||||||
<div class="mb-3">
|
<!-- <div class="mb-3"> -->
|
||||||
<label for="IPv4Address" class="form-label">IPv4 Address</label>
|
<!-- <label for="IPv4Address" class="form-label">IPv4 Address</label> -->
|
||||||
<input type="text" class="form-control" id="IPv4Address" name="IPv4Address" required>
|
<!-- <input type="text" class="form-control" id="IPv4Address" name="IPv4Address" required> -->
|
||||||
</div>
|
<!-- </div> -->
|
||||||
<div class="mb-3">
|
<!-- <div class="mb-3"> -->
|
||||||
<label for="initialContact" class="form-label">Initial Contact</label>
|
<!-- <label for="initialContact" class="form-label">Initial Contact</label> -->
|
||||||
<input type="datetime-local" class="form-control" id="initialContact" name="initialContact" required>
|
<!-- <input type="datetime-local" class="form-control" id="initialContact" name="initialContact" required> -->
|
||||||
</div>
|
<!-- </div> -->
|
||||||
<div class="mb-3">
|
<!-- <div class="mb-3"> -->
|
||||||
<label for="lastContact" class="form-label">Last Contact</label>
|
<!-- <label for="lastContact" class="form-label">Last Contact</label> -->
|
||||||
<input type="datetime-local" class="form-control" id="lastContact" name="lastContact" required>
|
<!-- <input type="datetime-local" class="form-control" id="lastContact" name="lastContact" required> -->
|
||||||
</div>
|
<!-- </div> -->
|
||||||
<button type="submit" class="btn btn-success">Add Agent</button>
|
<!-- <button type="submit" class="btn btn-success">Add Agent</button> -->
|
||||||
</form>
|
<!-- </form> -->
|
||||||
</div>
|
<!-- </div> -->
|
||||||
</div>
|
<!-- </div> -->
|
||||||
|
|
||||||
|
<!-- Logs Section -->
|
||||||
|
<h3>Logs</h3>
|
||||||
|
<div id="logs-container" hx-get="/logs" hx-target="#logs-container" hx-swap="innerHTML" hx-trigger="every 3s">
|
||||||
|
<!-- Logs will be injected here -->
|
||||||
|
<!-- </div> -->
|
||||||
|
<!-- <button hx-get="/logs" hx-target="#logs-container" hx-swap="innerHTML"> -->
|
||||||
|
<!-- Load Logs -->
|
||||||
|
<!-- </button> -->
|
||||||
|
<!-- <button hx-get="/logs" hx-target="#logs-container" hx-swap="innerHTML" hx-trigger="every 8s"> -->
|
||||||
|
<!-- Auto-Refresh Logs -->
|
||||||
|
<!-- </button> -->
|
||||||
|
<!-- </div> -->
|
||||||
|
|
||||||
<!-- Agent Details -->
|
<!-- Agent Details -->
|
||||||
<div class="col" id="agentDetails">
|
<div class="col" id="agentDetails">
|
||||||
|
|
|
@ -5,6 +5,23 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Web Server Logs</title>
|
<title>Web Server Logs</title>
|
||||||
<script src="https://unpkg.com/htmx.org@1.8.5"></script>
|
<script src="https://unpkg.com/htmx.org@1.8.5"></script>
|
||||||
|
<style>
|
||||||
|
.log-info {
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
.log-warning {
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
.log-error {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
.log-fatal {
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
.log-debug {
|
||||||
|
color: violet;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Web Server Logs</h1>
|
<h1>Web Server Logs</h1>
|
||||||
|
@ -13,12 +30,51 @@
|
||||||
<!-- Logs will be injected here -->
|
<!-- Logs will be injected here -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button hx-get="/logs" hx-target="#logs-container" hx-swap="outerHTML">
|
<button hx-get="/logs" hx-target="#logs-container" hx-swap="innerHTML">
|
||||||
Load Logs
|
Load Logs
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button hx-get="/logs" hx-target="#logs-container" hx-swap="outerHTML" hx-trigger="every 2s">
|
<button hx-get="/logs" hx-target="#logs-container" hx-swap="innerHTML" hx-trigger="every 2s">
|
||||||
Auto-Refresh Logs
|
Auto-Refresh Logs
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function renderLogs(logs) {
|
||||||
|
const container = document.getElementById('logs-container');
|
||||||
|
container.innerHTML = '';
|
||||||
|
|
||||||
|
logs.forEarch(log => {
|
||||||
|
const logElement = document.createElement('p');
|
||||||
|
logElement.innerHTML = `<strong>ts=${log.timestamp} level=${log.level}</strong> msg=${log.message}`;
|
||||||
|
|
||||||
|
if (log.level ==== 'INFO') {
|
||||||
|
logElement.classList.add('log-info');
|
||||||
|
} else if (log.level === 'WARNING') {
|
||||||
|
logElement.classList.add('log'warning);
|
||||||
|
} else if (log.level === 'ERROR') {
|
||||||
|
logElement.classList.add('log-warning');
|
||||||
|
} else if (log.level === 'FATAL') {
|
||||||
|
logElement.classList.add('log-fatal');
|
||||||
|
} else if (log.level === 'DEBUG') {
|
||||||
|
logElement.classList.add('log-debug');
|
||||||
|
}
|
||||||
|
|
||||||
|
container.appendChild(logElement);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchLogs() {
|
||||||
|
fetch('/logs')
|
||||||
|
.then(response -> response.json())
|
||||||
|
.then(data => renderLogs(data))
|
||||||
|
.catch(error -> console.error('Error fetching logs:', error));
|
||||||
|
}
|
||||||
|
|
||||||
|
document.body.addEventListener('htmx:afterSwap', function(event){
|
||||||
|
if (event.target.id === 'logs-container') {
|
||||||
|
fetchLogs();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
<!-- <td>{{.LastContact}}</td> -->
|
<!-- <td>{{.LastContact}}</td> -->
|
||||||
<!-- <td><span class="badge bg-success">Connected</span></td> -->
|
<!-- <td><span class="badge bg-success">Connected</span></td> -->
|
||||||
<!-- <td><span class="badge bg-danger">Disconnected</span></td> -->
|
<!-- <td><span class="badge bg-danger">Disconnected</span></td> -->
|
||||||
<td></td>
|
<td>{{.Status}}</td>
|
||||||
<td>
|
<td>
|
||||||
<button class="btn btn-warning" hx-get="/agents/{{.AgentID}}" hx-target="#agentDetails" hx-swap="innerHTML">View</button>
|
<button class="btn btn-warning" hx-get="/agents/{{.AgentID}}" hx-target="#agentDetails" hx-swap="innerHTML">View</button>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
{{range .}}
|
||||||
|
<p class="log-{{.Level}}">
|
||||||
|
<strong>ts={{.Timestamp}} level={{.Level}}</strong> msg="{{.Message}}"
|
||||||
|
</p>
|
||||||
|
{{end}}
|
Loading…
Reference in New Issue