bugfixing
This commit is contained in:
parent
6ea4d31109
commit
2056479224
105
main.go
105
main.go
|
@ -2,22 +2,23 @@ package main
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"fmt"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"log"
|
||||
"net/http"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"gontrol/src/randomname"
|
||||
"gontrol/src/server/database"
|
||||
"gontrol/src/logger"
|
||||
"gontrol/src/randomname"
|
||||
api "gontrol/src/server/api"
|
||||
"gontrol/src/server/database"
|
||||
websocketserver "gontrol/src/server/websocket"
|
||||
|
||||
"os/signal"
|
||||
|
@ -31,6 +32,8 @@ var (
|
|||
db *sql.DB
|
||||
)
|
||||
|
||||
|
||||
|
||||
type Config struct {
|
||||
Database struct {
|
||||
Username string `envconfig:"DB_USERNAME"`
|
||||
|
@ -55,22 +58,14 @@ func processError(err error) {
|
|||
|
||||
func init() {
|
||||
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{}) {
|
||||
t, err := template.ParseFiles(tmpl)
|
||||
if err != nil {
|
||||
|
@ -119,7 +114,24 @@ func getHomepage(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func listAgents(w http.ResponseWriter, r *http.Request) {
|
||||
var agents []api.Agent
|
||||
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 {
|
||||
http.Error(w, "Failed to fetch agents", http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -128,6 +140,23 @@ func listAgents(w http.ResponseWriter, r *http.Request) {
|
|||
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) {
|
||||
api.GetAgentNames(db, w, r)
|
||||
return
|
||||
|
@ -139,25 +168,38 @@ func getAgentIds(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func logsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var logs[] logger.LogEntry
|
||||
logs, err := logger.FetchLogs(10)
|
||||
|
||||
// for i, logLine := range logs {
|
||||
// logs[i].Message = strings.ReplaceAll(logLine.Message, `"`, `\"`)
|
||||
// }
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, "Error fetching logs", http.StatusInternalServerError)
|
||||
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() {
|
||||
|
||||
// sqlite3 has been initialized in init()
|
||||
defer logger.CloseDB()
|
||||
|
||||
var cfg Config
|
||||
readEnv(&cfg)
|
||||
|
||||
|
@ -176,18 +218,11 @@ func main() {
|
|||
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()
|
||||
fmt.Println(name)
|
||||
fmt.Sprintf("Server instance: %s", name)
|
||||
|
||||
webServer := &http.Server {
|
||||
Addr: ":3333",
|
||||
|
@ -202,7 +237,7 @@ func main() {
|
|||
if err := websocketServer.ListenAndServe(); err != http.ErrServerClosed {
|
||||
log.Fatalf("Websocket server failed: %s", err)
|
||||
}
|
||||
err = logger.InsertLog(logger.Info, logLine)
|
||||
err := logger.InsertLog(logger.Info, logLine)
|
||||
if err != nil {
|
||||
log.Println("Error inserting log:", err)
|
||||
}
|
||||
|
@ -218,7 +253,7 @@ func main() {
|
|||
log.Fatalf("Web server failed: %s", err)
|
||||
}
|
||||
|
||||
err = logger.InsertLog(logger.Info, logLine)
|
||||
err := logger.InsertLog(logger.Info, logLine)
|
||||
if err != nil {
|
||||
log.Println("Error inserting log:", err)
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
// "net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
@ -17,16 +18,16 @@ var (
|
|||
lite_dbMutex sync.Mutex
|
||||
|
||||
logMutex sync.Mutex
|
||||
logLimit = 10
|
||||
logLimit = 100
|
||||
// LogEntries []string
|
||||
)
|
||||
|
||||
const (
|
||||
Debug LogLevel = "DEBUG"
|
||||
Info LogLevel = "INFO"
|
||||
Warning LogLevel = "WARNING"
|
||||
Error LogLevel = "ERROR"
|
||||
Fatal LogLevel = "FATAL"
|
||||
Debug LogLevel = "debug"
|
||||
Info LogLevel = "info"
|
||||
Warning LogLevel = "warning"
|
||||
Error LogLevel = "error"
|
||||
Fatal LogLevel = "fatal"
|
||||
)
|
||||
|
||||
type LogLevel string
|
||||
|
@ -74,6 +75,8 @@ func InsertLog(level LogLevel, message string) error {
|
|||
return fmt.Errorf("Error starting transaction: %w", err)
|
||||
}
|
||||
|
||||
message = strings.ReplaceAll(message, `"`, `\"`)
|
||||
|
||||
// insertQuery := `INSERT INTO logs (message) VALUES (?)`
|
||||
insertQuery := `INSERT INTO logs (message, level) VALUES (?, ?)`
|
||||
// _, 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)
|
||||
if err != nil {
|
||||
log.Printf("Error registering agent: %v", err)
|
||||
logger.InsertLog(logger.Error, fmt.Sprintf("Error registering agent: %v", err))
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusCreated {
|
||||
logLine := fmt.Sprintf("Agent %s successfully registered.", agentName)
|
||||
log.Printf(logLine)
|
||||
// logLine = logger.ToLog(logLine)
|
||||
logger.InsertLog(logger.Info, logLine)
|
||||
log.Printf("Agent %s successfully registered.", agentName)
|
||||
logger.InsertLog(logger.Info, fmt.Sprintf("Agent %s successfully registered.", agentName))
|
||||
return nil
|
||||
} else if resp.StatusCode == http.StatusOK {
|
||||
log.Printf("Agent %s already registered.", agentName)
|
||||
logger.InsertLog(logger.Info, fmt.Sprintf("Agent %s already registered.", agentName))
|
||||
return nil
|
||||
} else {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -87,6 +88,7 @@ func getAgentDetails(agentId string) (*api.Agent, error) {
|
|||
resp, err := http.Get(agentURL)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
@ -94,6 +96,7 @@ func getAgentDetails(agentId string) (*api.Agent, error) {
|
|||
doc, err := goquery.NewDocumentFromReader(resp.Body)
|
||||
if err != nil {
|
||||
log.Printf("Failed to parse HTML: %s", err)
|
||||
logger.InsertLog(logger.Error, fmt.Sprintf("Failed to parse HTML: %s", 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:")))
|
||||
if err != nil {
|
||||
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:") {
|
||||
agent.AgentName = strings.TrimSpace(strings.TrimPrefix(text, "Name:"))
|
||||
|
@ -130,24 +134,28 @@ func getAgentIds() ([]string, error) {
|
|||
resp, err := http.Get(idURL)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
log.Printf("Unexpected status code: %d", resp.StatusCode)
|
||||
logger.InsertLog(logger.Info, fmt.Sprintf("Unexpected status code: %d", resp.StatusCode))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
var agentIds []string
|
||||
if err := json.Unmarshal(body, &agentIds); err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -159,6 +167,7 @@ func (wsh webSocketHandler) ServeHTTP(w http.ResponseWriter, r *http.Request){
|
|||
c, err := wsh.upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -183,13 +192,13 @@ func (wsh webSocketHandler) ServeHTTP(w http.ResponseWriter, r *http.Request){
|
|||
|
||||
if agentName == "" || agentIP == "" {
|
||||
log.Printf("Missing agentName or IPv4Address in query parameters")
|
||||
logger.InsertLog(logger.Info, fmt.Sprintf("Missing agentName or IPv4Address in query parameters"))
|
||||
c.Close()
|
||||
return
|
||||
}
|
||||
|
||||
logLine := fmt.Sprintf("Agent %s connected: %s (%s)", agentId, agentName, agentIP)
|
||||
log.Printf(logLine)
|
||||
logger.InsertLog(logger.Info, logLine)
|
||||
log.Printf("Agent %s connected: %s (%s)", agentId, agentName, agentIP)
|
||||
logger.InsertLog(logger.Info, fmt.Sprintf("Agent %s connected: %s (%s)", agentId, agentName, agentIP))
|
||||
|
||||
agentSocketsMutex.Lock()
|
||||
agentSockets[agentName] = c
|
||||
|
@ -200,22 +209,19 @@ func (wsh webSocketHandler) ServeHTTP(w http.ResponseWriter, r *http.Request){
|
|||
delete(agentSockets, agentName)
|
||||
agentSocketsMutex.Unlock()
|
||||
c.Close()
|
||||
logLine := fmt.Sprintf("Agent disconnected: %s (%s)", agentName, agentIP)
|
||||
log.Printf(logLine)
|
||||
logger.InsertLog(logger.Info, logLine)
|
||||
log.Printf("Agent disconnected: %s (%s)", agentName, agentIP)
|
||||
logger.InsertLog(logger.Info, fmt.Sprintf("Agent disconnected: %s (%s)", agentName, agentIP))
|
||||
}()
|
||||
|
||||
for {
|
||||
_, message, err := c.ReadMessage()
|
||||
if err != nil {
|
||||
logLine := fmt.Sprintf("Error reading from agent %s: %v", agentName, err)
|
||||
log.Printf(logLine)
|
||||
logger.InsertLog(logger.Error, logLine)
|
||||
log.Printf("Error reading from agent %s: %v", agentName, err)
|
||||
logger.InsertLog(logger.Error, fmt.Sprintf("Error reading from agent %s: %v", agentName, err))
|
||||
break
|
||||
}
|
||||
logLine := fmt.Sprintf("Message from agent %s: %s", agentName, message)
|
||||
log.Printf(logLine)
|
||||
logger.InsertLog(logger.Debug, logLine)
|
||||
log.Printf("Message from agent %s: %s", agentName, message)
|
||||
logger.InsertLog(logger.Info, fmt.Sprintf("Message from agent %s: %s", agentName, message))
|
||||
|
||||
if ch, ok := responseChannels.Load(agentName); ok {
|
||||
responseChan := ch.(chan string)
|
||||
|
@ -233,6 +239,7 @@ var executeCommand http.HandlerFunc = func(w http.ResponseWriter, r *http.Reques
|
|||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid form data", http.StatusBadRequest)
|
||||
logger.InsertLog(logger.Info, "Invalid form data")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -245,6 +252,7 @@ var executeCommand http.HandlerFunc = func(w http.ResponseWriter, r *http.Reques
|
|||
|
||||
if !ok {
|
||||
http.Error(w, "Agent not connected", http.StatusNotFound)
|
||||
logger.InsertLog(logger.Info, "Agent not connected")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -262,6 +270,7 @@ var executeCommand http.HandlerFunc = func(w http.ResponseWriter, r *http.Reques
|
|||
err = conn.WriteMessage(websocket.TextMessage, messageBytes)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to send command to the agent", http.StatusInternalServerError)
|
||||
logger.InsertLog(logger.Error, "Failed to send command to the agent")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -275,13 +284,15 @@ var executeCommand http.HandlerFunc = func(w http.ResponseWriter, r *http.Reques
|
|||
payload, ok := parsedResponse["payload"]
|
||||
if !ok {
|
||||
http.Error(w, "Invalid response structure", http.StatusInternalServerError)
|
||||
logger.InsertLog(logger.Error, "Invalid response structure")
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.Write([]byte(payload))
|
||||
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>
|
||||
<script>
|
||||
// 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', () => {
|
||||
fetch('/agentNames')
|
||||
.then(response => response.json())
|
||||
.then(agentNames => {
|
||||
const dropdown = document.getElementById('agentName');
|
||||
agentNames.forEach(name => {
|
||||
const option = document.createElement('option');
|
||||
document.body.addEventListener('htmx:afterSwap', function(event) {
|
||||
if (event.target.id === "agentList") {
|
||||
updateAgentDropdown();
|
||||
}
|
||||
});
|
||||
|
||||
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.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>';
|
||||
select.appendChild(option);
|
||||
}
|
||||
}
|
||||
|
||||
if (status === "Disconnected") {
|
||||
row.cells[4].innerHTML = '<span class="badge bg-danger">Disconnected</span>';
|
||||
const option = Array.from(select.options).find(opt => opt.value === name);
|
||||
if(option) {
|
||||
select.removeChild(option);
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(error => console.error('Error fetching agent names:', error));
|
||||
};
|
||||
updateAgentStatuses();
|
||||
setInterval(updateAgentStatuses, 1000);
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
<style>
|
||||
.log-info {
|
||||
color: green;
|
||||
}
|
||||
.log-warning {
|
||||
color: orange;
|
||||
}
|
||||
.log-error {
|
||||
color: red;
|
||||
}
|
||||
.log-fatal {
|
||||
color: blue;
|
||||
}
|
||||
.log-debug {
|
||||
color: violet;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
|
@ -58,15 +132,17 @@
|
|||
<h2>Agents</h2>
|
||||
|
||||
<!-- 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" 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> -->
|
||||
<!-- Agent Commands -->
|
||||
<div id="agentCommands">
|
||||
<h3>Command Execution</h3>
|
||||
<form hx-post="http://localhost:5555/executeCommand" hx-target="#commandOutput" hx-encoding="application/x-www-form-urlencoded" hx-swap="innerHTML">
|
||||
<div class="mb-3">
|
||||
<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 -->
|
||||
</select>
|
||||
</div>
|
||||
|
@ -80,33 +156,46 @@
|
|||
</div>
|
||||
|
||||
<!-- Add Agent Form -->
|
||||
<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">
|
||||
<form hx-post="/agents" hx-target="#agentList" hx-swap="innerHTML">
|
||||
<div class="mb-3">
|
||||
<label for="agentId" class="form-label">Agent Id</label>
|
||||
<input type="text" class="form-control" id="agentId" name="agentId" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="agentName" class="form-label">Agent Name</label>
|
||||
<input type="text" class="form-control" id="agentName" name="agentName" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="IPv4Address" class="form-label">IPv4 Address</label>
|
||||
<input type="text" class="form-control" id="IPv4Address" name="IPv4Address" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="initialContact" class="form-label">Initial Contact</label>
|
||||
<input type="datetime-local" class="form-control" id="initialContact" name="initialContact" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="lastContact" class="form-label">Last Contact</label>
|
||||
<input type="datetime-local" class="form-control" id="lastContact" name="lastContact" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-success">Add Agent</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <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"> -->
|
||||
<!-- <form hx-post="/agents" hx-target="#agentList" hx-swap="innerHTML"> -->
|
||||
<!-- <div class="mb-3"> -->
|
||||
<!-- <label for="agentId" class="form-label">Agent Id</label> -->
|
||||
<!-- <input type="text" class="form-control" id="agentId" name="agentId" required> -->
|
||||
<!-- </div> -->
|
||||
<!-- <div class="mb-3"> -->
|
||||
<!-- <label for="agentName" class="form-label">Agent Name</label> -->
|
||||
<!-- <input type="text" class="form-control" id="agentName" name="agentName" required> -->
|
||||
<!-- </div> -->
|
||||
<!-- <div class="mb-3"> -->
|
||||
<!-- <label for="IPv4Address" class="form-label">IPv4 Address</label> -->
|
||||
<!-- <input type="text" class="form-control" id="IPv4Address" name="IPv4Address" required> -->
|
||||
<!-- </div> -->
|
||||
<!-- <div class="mb-3"> -->
|
||||
<!-- <label for="initialContact" class="form-label">Initial Contact</label> -->
|
||||
<!-- <input type="datetime-local" class="form-control" id="initialContact" name="initialContact" required> -->
|
||||
<!-- </div> -->
|
||||
<!-- <div class="mb-3"> -->
|
||||
<!-- <label for="lastContact" class="form-label">Last Contact</label> -->
|
||||
<!-- <input type="datetime-local" class="form-control" id="lastContact" name="lastContact" required> -->
|
||||
<!-- </div> -->
|
||||
<!-- <button type="submit" class="btn btn-success">Add Agent</button> -->
|
||||
<!-- </form> -->
|
||||
<!-- </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 -->
|
||||
<div class="col" id="agentDetails">
|
||||
|
|
|
@ -5,6 +5,23 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Web Server Logs</title>
|
||||
<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>
|
||||
<body>
|
||||
<h1>Web Server Logs</h1>
|
||||
|
@ -13,12 +30,51 @@
|
|||
<!-- Logs will be injected here -->
|
||||
</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
|
||||
</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
|
||||
</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>
|
||||
</html>
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
<!-- <td>{{.LastContact}}</td> -->
|
||||
<!-- <td><span class="badge bg-success">Connected</span></td> -->
|
||||
<!-- <td><span class="badge bg-danger">Disconnected</span></td> -->
|
||||
<td></td>
|
||||
<td>{{.Status}}</td>
|
||||
<td>
|
||||
<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