diff --git a/.gitignore b/.gitignore
index 5f9b08d..1373fbd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,4 @@ build/
build/*
pkg/*
agents/agents
+logs.db
diff --git a/agents/agent.go b/agents/agent.go
index 5d6b6c4..8d7f819 100644
--- a/agents/agent.go
+++ b/agents/agent.go
@@ -11,6 +11,7 @@ import (
"math/rand"
"math"
"strconv"
+ // "gontrol/src/logger"
"github.com/gorilla/websocket"
)
@@ -57,6 +58,7 @@ func registerAgent(agentName, agentId, agentIp, agentType string) error {
}
log.Printf("Agent %s successfully registered.", agentName)
+ logger.LogEntries = append(logger.LogEntries, fmt.Sprintf("%s Agent successfully registered.", time.Now().Format(time.RFC3339)))
return nil
}
@@ -67,6 +69,7 @@ func connectToWebSocket(agentName, agentId, agentIp, agentType string) error {
conn, _, err = websocket.DefaultDialer.Dial(wsURL, nil)
if err == nil {
log.Println("WeSocket connection established")
+ // logger.LogEntries = append(logger.LogEntries, fmt.Sprintf("%s websocket established", time.Now().Format(time.RFC3339)))
return nil
}
diff --git a/go.mod b/go.mod
index 0b9f0c9..7eecfcd 100644
--- a/go.mod
+++ b/go.mod
@@ -7,6 +7,7 @@ require (
github.com/go-sql-driver/mysql v1.8.1
github.com/gorilla/websocket v1.5.3
github.com/kelseyhightower/envconfig v1.4.0
+ github.com/mattn/go-sqlite3 v1.14.28
)
require (
diff --git a/go.sum b/go.sum
index f6be390..a87bc7f 100644
--- a/go.sum
+++ b/go.sum
@@ -11,6 +11,8 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
+github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
+github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
diff --git a/main.go b/main.go
index 04de929..765df01 100644
--- a/main.go
+++ b/main.go
@@ -16,6 +16,7 @@ import (
"gontrol/src/randomname"
"gontrol/src/server/database"
+ "gontrol/src/logger"
api "gontrol/src/server/api"
websocketserver "gontrol/src/server/websocket"
@@ -25,8 +26,10 @@ import (
"github.com/kelseyhightower/envconfig"
)
-var tmpl *template.Template
-var db *sql.DB
+var (
+ tmpl *template.Template
+ db *sql.DB
+)
type Config struct {
Database struct {
@@ -135,6 +138,24 @@ func getAgentIds(w http.ResponseWriter, r *http.Request) {
return
}
+func logsHandler(w http.ResponseWriter, r *http.Request) {
+
+ logs, err := logger.FetchLogs(10)
+ if err != nil {
+ http.Error(w, "Error fetching logs", http.StatusInternalServerError)
+ return
+ }
+
+ fmt.Fprintf(w, "
")
+ // for _, logEntry := range logsToSend {
+ for _, logEntry := range logs {
+ fmt.Fprintf(w, "
[%s] [%s] %s
", logEntry.Timestamp, logEntry.Level, logEntry.Message)
+ }
+ fmt.Fprintf(w, "
")
+
+}
+
+
func main() {
var cfg Config
@@ -153,8 +174,16 @@ func main() {
webMux.HandleFunc("/agentNames", getAgentNames)
webMux.HandleFunc("/agentIds", getAgentIds)
webMux.HandleFunc("/agents/{agentId}", agentsHandler)
+ 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)
defer db.Close()
name := randomname.GenerateRandomName()
@@ -168,20 +197,31 @@ func main() {
wg.Add(1)
go func() {
defer wg.Done()
- log.Println("Websocket server is running on port 5555")
+ logLine := "Websocket server is running on port 5555"
+ log.Println(logLine)
if err := websocketServer.ListenAndServe(); err != http.ErrServerClosed {
log.Fatalf("Websocket server failed: %s", err)
}
+ err = logger.InsertLog(logger.Info, logLine)
+ if err != nil {
+ log.Println("Error inserting log:", err)
+ }
}()
wg.Add(1)
go func() {
defer wg.Done()
- log.Println("Web server is running on port 3333")
+ logLine := "Web server is running on port 3333"
+ log.Println(logLine)
if err := webServer.ListenAndServe(); err != http.ErrServerClosed {
log.Fatalf("Web server failed: %s", err)
}
+
+ err = logger.InsertLog(logger.Info, logLine)
+ if err != nil {
+ log.Println("Error inserting log:", err)
+ }
}()
shutdownCh := make(chan os.Signal, 1)
diff --git a/src/logger/logger.go b/src/logger/logger.go
new file mode 100644
index 0000000..c4158c3
--- /dev/null
+++ b/src/logger/logger.go
@@ -0,0 +1,127 @@
+package logger
+
+import (
+ "database/sql"
+ "fmt"
+ "log"
+ // "net/http"
+ "sync"
+ "time"
+
+ _ "github.com/mattn/go-sqlite3"
+)
+
+
+var (
+ Lite_db *sql.DB
+ lite_dbMutex sync.Mutex
+
+ logMutex sync.Mutex
+ logLimit = 10
+ // LogEntries []string
+)
+
+const (
+ Debug LogLevel = "DEBUG"
+ Info LogLevel = "INFO"
+ Warning LogLevel = "WARNING"
+ Error LogLevel = "ERROR"
+ Fatal LogLevel = "FATAL"
+)
+
+type LogLevel string
+
+type LogEntry struct {
+ Message string
+ Timestamp string
+ Level LogLevel
+}
+
+func ToLog(logLine string) string {
+ log := fmt.Sprintf("%s",time.Now().Format(time.RFC3339) + " " + logLine)
+ return log
+}
+
+func InitDB(dbPath string) error {
+ var err error
+ Lite_db, err = sql.Open("sqlite3", dbPath)
+ if err != nil {
+ return fmt.Errorf("Error opening DB: %w", err)
+ }
+
+ CreateTableQuery := `CREATE TABLE IF NOT EXISTS logs (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ message TEXT,
+ timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
+ level TEXT
+ );`
+
+ _, err = Lite_db.Exec(CreateTableQuery)
+ if err != nil {
+ return fmt.Errorf("Error creating table: %w", err)
+ }
+
+ return nil
+}
+
+func InsertLog(level LogLevel, message string) error {
+ lite_dbMutex.Lock()
+ defer lite_dbMutex.Unlock()
+
+ // Future use may fulfill multiple transactions, a transaction is used
+ tx, err := Lite_db.Begin()
+ if err != nil {
+ return fmt.Errorf("Error starting transaction: %w", err)
+ }
+
+ // insertQuery := `INSERT INTO logs (message) VALUES (?)`
+ insertQuery := `INSERT INTO logs (message, level) VALUES (?, ?)`
+ // _, err := Lite_db.Exec(insertQuery, message, level)
+ _, err = tx.Exec(insertQuery, message, level)
+
+ if err != nil {
+ tx.Rollback()
+ return fmt.Errorf("Error inserting log: %v", err)
+ }
+
+ err = tx.Commit()
+ if err != nil {
+ return fmt.Errorf("Error committing transaction: %w", err)
+ }
+
+ return nil
+}
+
+func FetchLogs(limit int) ([]LogEntry, error) {
+ lite_dbMutex.Lock()
+ defer lite_dbMutex.Unlock()
+
+ query := `SELECT timestamp, level, message FROM logs ORDER BY timestamp DESC LIMIT ?`
+ rows, err := Lite_db.Query(query, limit)
+ if err != nil {
+ return nil, fmt.Errorf("Error fetching logs: %w", err)
+ }
+ defer rows.Close()
+
+ // var logs []string
+ var logs []LogEntry
+ for rows.Next() {
+ // var message string
+ var logEntry LogEntry
+ if err := rows.Scan( &logEntry.Timestamp, &logEntry.Level, &logEntry.Message); err != nil {
+ return nil, fmt.Errorf("Error scanning row: %w", err)
+ }
+ logs = append(logs, logEntry)
+ }
+
+ return logs, nil
+}
+
+func CloseDB() {
+ if Lite_db != nil {
+ err := Lite_db.Close()
+ if err != nil {
+ log.Printf("Error closing database: %v", err)
+ }
+ }
+}
diff --git a/src/server/api/agentApi.go b/src/server/api/agentApi.go
index 2f12685..a43bb41 100644
--- a/src/server/api/agentApi.go
+++ b/src/server/api/agentApi.go
@@ -6,6 +6,7 @@ import (
"encoding/json"
"strconv"
"log"
+ // "gontrol/src/logger"
_ "github.com/go-sql-driver/mysql"
)
diff --git a/src/server/websocket/websocketServer.go b/src/server/websocket/websocketServer.go
index db08b9d..56a9479 100644
--- a/src/server/websocket/websocketServer.go
+++ b/src/server/websocket/websocketServer.go
@@ -3,6 +3,8 @@ package websocketserver
import (
"database/sql"
"encoding/json"
+ "fmt"
+ "gontrol/src/logger"
"gontrol/src/randomname"
"gontrol/src/server/api"
"io"
@@ -63,7 +65,10 @@ func registerAgent(agentName, agentId, agentIp, agentType string) error {
defer resp.Body.Close()
if resp.StatusCode == http.StatusCreated {
- log.Printf("Agent %s successfully registered.", agentName)
+ logLine := fmt.Sprintf("Agent %s successfully registered.", agentName)
+ log.Printf(logLine)
+ // logLine = logger.ToLog(logLine)
+ logger.InsertLog(logger.Info, logLine)
return nil
} else if resp.StatusCode == http.StatusOK {
log.Printf("Agent %s already registered.", agentName)
@@ -81,14 +86,14 @@ func getAgentDetails(agentId string) (*api.Agent, error) {
// var ids []string
resp, err := http.Get(agentURL)
if err != nil {
- log.Printf("Failed to make GET request: %w", err)
+ log.Printf("Failed to make GET request: %s", err)
return nil, err
}
defer resp.Body.Close()
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
- log.Printf("Failed to parse HTML: %w", err)
+ log.Printf("Failed to parse HTML: %s", err)
return nil, err
}
@@ -98,7 +103,7 @@ func getAgentDetails(agentId string) (*api.Agent, error) {
if strings.HasPrefix(text, "ID:") {
agent.AgentID, err = strconv.Atoi(strings.TrimSpace(strings.TrimPrefix(text, "ID:")))
if err != nil {
- log.Printf("Converting string to integer failed in getAgentDetails(): %w", err)
+ log.Printf("Converting string to integer failed in getAgentDetails(): %s", err)
}
} else if strings.HasPrefix(text, "Name:") {
agent.AgentName = strings.TrimSpace(strings.TrimPrefix(text, "Name:"))
@@ -124,7 +129,7 @@ func getAgentIds() ([]string, error) {
// var ids []string
resp, err := http.Get(idURL)
if err != nil {
- log.Printf("Failed to make GET request: %w", err)
+ log.Printf("Failed to make GET request: %s", err)
return nil, err
}
defer resp.Body.Close()
@@ -136,13 +141,13 @@ func getAgentIds() ([]string, error) {
body, err := io.ReadAll(resp.Body)
if err != nil {
- log.Printf("Failed to read response body: %w", err)
+ log.Printf("Failed to read response body: %s", err)
return nil, err
}
var agentIds []string
if err := json.Unmarshal(body, &agentIds); err != nil {
- log.Printf("Failed to parse JSON response: %w", err)
+ log.Printf("Failed to parse JSON response: %s", err)
return nil, err
}
@@ -182,7 +187,9 @@ func (wsh webSocketHandler) ServeHTTP(w http.ResponseWriter, r *http.Request){
return
}
- log.Printf("Agent %s connected: %s (%s)", agentId, agentName, agentIP)
+ logLine := fmt.Sprintf("Agent %s connected: %s (%s)", agentId, agentName, agentIP)
+ log.Printf(logLine)
+ logger.InsertLog(logger.Info, logLine)
agentSocketsMutex.Lock()
agentSockets[agentName] = c
@@ -193,17 +200,22 @@ func (wsh webSocketHandler) ServeHTTP(w http.ResponseWriter, r *http.Request){
delete(agentSockets, agentName)
agentSocketsMutex.Unlock()
c.Close()
- log.Printf("Agent disconnected: %s (%s)", agentName, agentIP)
+ logLine := fmt.Sprintf("Agent disconnected: %s (%s)", agentName, agentIP)
+ log.Printf(logLine)
+ logger.InsertLog(logger.Info, logLine)
}()
for {
_, message, err := c.ReadMessage()
if err != nil {
- log.Printf("Error reading from agent %s: %v", agentName, err)
+ logLine := fmt.Sprintf("Error reading from agent %s: %v", agentName, err)
+ log.Printf(logLine)
+ logger.InsertLog(logger.Error, logLine)
break
}
- log.Printf("Message from agent %s: %s", agentName, message)
-
+ logLine := fmt.Sprintf("Message from agent %s: %s", agentName, message)
+ log.Printf(logLine)
+ logger.InsertLog(logger.Debug, logLine)
if ch, ok := responseChannels.Load(agentName); ok {
responseChan := ch.(chan string)
@@ -274,8 +286,6 @@ var executeCommand http.HandlerFunc = func(w http.ResponseWriter, r *http.Reques
}
func Server() (*http.Server) {
-
-
webSocketHandler := webSocketHandler {
upgrader: websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
@@ -286,7 +296,7 @@ func Server() (*http.Server) {
corsMiddleware := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Access-Control-Allow-Origin", "*") // Allow the WebUI origin
+ w.Header().Set("Access-Control-Allow-Origin", "*") // Allow the WebUI origin, this needs to be changed before prod
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, HX-Current-URL, HX-Request, HX-Target, HX-Trigger, HX-Trigger-Name")
if r.Method == "OPTIONS" {
diff --git a/templates/index.html b/templates/index.html
index 4e12b85..ba16f96 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -10,6 +10,7 @@
g2: gommand & gontrol
diff --git a/templates/logs.html b/templates/logs.html
new file mode 100644
index 0000000..bafd305
--- /dev/null
+++ b/templates/logs.html
@@ -0,0 +1,24 @@
+
+
+
+
+
+ Web Server Logs
+
+
+
+ Web Server Logs
+
+
+
+
+
+
+
+
+
+