282 lines
7.8 KiB
Go
282 lines
7.8 KiB
Go
package api
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"log"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/cynfo/rmm-backend/db"
|
|
"github.com/cynfo/rmm-backend/models"
|
|
"github.com/cynfo/rmm-backend/ws"
|
|
)
|
|
|
|
type Handler struct {
|
|
db *db.Database
|
|
}
|
|
|
|
func NewHandler(database *db.Database) *Handler {
|
|
return &Handler{db: database}
|
|
}
|
|
|
|
// SetupRoutes - Alle API-Routes registrieren
|
|
func (h *Handler) SetupRoutes(mux *http.ServeMux, hub *ws.Hub) {
|
|
// Bestehende Agent-Routes
|
|
mux.HandleFunc("POST /api/v1/agent/register", h.registerAgent)
|
|
mux.HandleFunc("POST /api/v1/agent/heartbeat", h.heartbeat)
|
|
mux.HandleFunc("GET /api/v1/agents", h.listAgents)
|
|
mux.HandleFunc("GET /api/v1/agents/events", h.getAllAgentEvents)
|
|
mux.HandleFunc("GET /api/v1/agents/{id}", h.getAgent)
|
|
mux.HandleFunc("GET /api/v1/agents/{id}/events", h.getAgentEvents)
|
|
mux.HandleFunc("GET /api/v1/agents/{id}/system", h.getSystemData)
|
|
mux.HandleFunc("DELETE /api/v1/agents/{id}", h.deleteAgent)
|
|
|
|
// Customer-Routes
|
|
mux.HandleFunc("GET /api/v1/customers", h.listCustomers)
|
|
mux.HandleFunc("POST /api/v1/customers", h.createCustomer)
|
|
mux.HandleFunc("GET /api/v1/customers/{id}", h.getCustomer)
|
|
mux.HandleFunc("PUT /api/v1/customers/{id}", h.updateCustomer)
|
|
mux.HandleFunc("DELETE /api/v1/customers/{id}", h.deleteCustomer)
|
|
mux.HandleFunc("GET /api/v1/customers/{id}/agents", h.getCustomerAgents)
|
|
|
|
// Agent-Customer Zuordnung
|
|
mux.HandleFunc("PUT /api/v1/agents/{id}/customer", h.assignAgentCustomer)
|
|
mux.HandleFunc("DELETE /api/v1/agents/{id}/customer", h.unassignAgentCustomer)
|
|
|
|
// User-Routes
|
|
mux.HandleFunc("GET /api/v1/users", h.listUsers)
|
|
mux.HandleFunc("POST /api/v1/users", h.createUser)
|
|
mux.HandleFunc("DELETE /api/v1/users/{id}", h.deleteUser)
|
|
|
|
// WebSocket und Tunnel-Routes
|
|
h.setupTunnelRoutes(mux, hub)
|
|
}
|
|
|
|
func generateID() string {
|
|
b := make([]byte, 16)
|
|
rand.Read(b)
|
|
return hex.EncodeToString(b)
|
|
}
|
|
|
|
func writeJSON(w http.ResponseWriter, status int, v interface{}) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(status)
|
|
json.NewEncoder(w).Encode(v)
|
|
}
|
|
|
|
func writeError(w http.ResponseWriter, status int, msg string) {
|
|
writeJSON(w, status, map[string]string{"error": msg})
|
|
}
|
|
|
|
// POST /api/v1/agent/register
|
|
func (h *Handler) registerAgent(w http.ResponseWriter, r *http.Request) {
|
|
var req models.RegisterRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
writeError(w, http.StatusBadRequest, "Ungueltige Anfrage")
|
|
return
|
|
}
|
|
|
|
if req.Name == "" || req.Hostname == "" {
|
|
writeError(w, http.StatusBadRequest, "Name und Hostname sind erforderlich")
|
|
return
|
|
}
|
|
|
|
// Agent-ID bestimmen: vom Agent mitgeliefert > nach Hostname suchen > neu generieren
|
|
agentID := req.AgentID
|
|
isExisting := false
|
|
|
|
if agentID != "" {
|
|
// Pruefen ob die ID schon in der DB existiert
|
|
if existing, _, err := h.db.GetAgent(agentID); err == nil && existing != nil {
|
|
isExisting = true
|
|
}
|
|
}
|
|
|
|
if agentID == "" {
|
|
// Nach bestehendem Agent mit gleichem Hostname suchen
|
|
if existing, err := h.db.GetAgentByHostname(req.Hostname); err == nil && existing != nil {
|
|
agentID = existing.ID
|
|
isExisting = true
|
|
}
|
|
}
|
|
|
|
if agentID == "" {
|
|
agentID = generateID()
|
|
}
|
|
|
|
agent := &models.Agent{
|
|
ID: agentID,
|
|
Name: req.Name,
|
|
Hostname: req.Hostname,
|
|
IP: req.IP,
|
|
OPNsenseVersion: req.OPNsenseVersion,
|
|
RegisteredAt: time.Now().UTC(),
|
|
}
|
|
|
|
if err := h.db.RegisterAgent(agent); err != nil {
|
|
log.Printf("Fehler bei Registrierung: %v", err)
|
|
writeError(w, http.StatusInternalServerError, "Registrierung fehlgeschlagen")
|
|
return
|
|
}
|
|
|
|
action := "registriert"
|
|
if isExisting {
|
|
action = "re-registriert"
|
|
}
|
|
log.Printf("Agent %s: %s (%s)", action, agent.Name, agent.ID)
|
|
writeJSON(w, http.StatusCreated, models.RegisterResponse{
|
|
ID: agent.ID,
|
|
Message: "Agent erfolgreich registriert",
|
|
})
|
|
}
|
|
|
|
// POST /api/v1/agent/heartbeat
|
|
func (h *Handler) heartbeat(w http.ResponseWriter, r *http.Request) {
|
|
var req models.HeartbeatRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
writeError(w, http.StatusBadRequest, "Ungueltige Anfrage")
|
|
return
|
|
}
|
|
|
|
if req.AgentID == "" {
|
|
writeError(w, http.StatusBadRequest, "agent_id erforderlich")
|
|
return
|
|
}
|
|
|
|
if err := h.db.SaveSystemData(req.AgentID, &req.SystemData); err != nil {
|
|
if strings.Contains(err.Error(), "nicht gefunden") {
|
|
writeError(w, http.StatusNotFound, "Agent nicht gefunden")
|
|
return
|
|
}
|
|
log.Printf("Fehler bei Heartbeat: %v", err)
|
|
writeError(w, http.StatusInternalServerError, "Heartbeat fehlgeschlagen")
|
|
return
|
|
}
|
|
|
|
writeJSON(w, http.StatusOK, models.HeartbeatResponse{Message: "OK"})
|
|
}
|
|
|
|
// GET /api/v1/agents
|
|
func (h *Handler) listAgents(w http.ResponseWriter, r *http.Request) {
|
|
agents, err := h.db.GetAgents()
|
|
if err != nil {
|
|
log.Printf("Fehler beim Laden der Agents: %v", err)
|
|
writeError(w, http.StatusInternalServerError, "Fehler beim Laden")
|
|
return
|
|
}
|
|
if agents == nil {
|
|
agents = []models.Agent{}
|
|
}
|
|
|
|
// Status fuer jeden Agent berechnen
|
|
var response []models.AgentResponse
|
|
for _, a := range agents {
|
|
response = append(response, models.AgentResponse{
|
|
Agent: a,
|
|
Status: models.CalculateAgentStatus(a.LastHeartbeat),
|
|
})
|
|
}
|
|
if response == nil {
|
|
response = []models.AgentResponse{}
|
|
}
|
|
writeJSON(w, http.StatusOK, response)
|
|
}
|
|
|
|
// GET /api/v1/agents/{id}
|
|
func (h *Handler) getAgent(w http.ResponseWriter, r *http.Request) {
|
|
id := r.PathValue("id")
|
|
agent, sysData, err := h.db.GetAgent(id)
|
|
if err != nil {
|
|
log.Printf("Fehler: %v", err)
|
|
writeError(w, http.StatusInternalServerError, "Fehler beim Laden")
|
|
return
|
|
}
|
|
if agent == nil {
|
|
writeError(w, http.StatusNotFound, "Agent nicht gefunden")
|
|
return
|
|
}
|
|
|
|
result := map[string]interface{}{
|
|
"agent": agent,
|
|
"status": models.CalculateAgentStatus(agent.LastHeartbeat),
|
|
"system_data": sysData,
|
|
}
|
|
writeJSON(w, http.StatusOK, result)
|
|
}
|
|
|
|
// GET /api/v1/agents/{id}/system
|
|
func (h *Handler) getSystemData(w http.ResponseWriter, r *http.Request) {
|
|
id := r.PathValue("id")
|
|
data, err := h.db.GetSystemData(id)
|
|
if err != nil {
|
|
log.Printf("Fehler: %v", err)
|
|
writeError(w, http.StatusInternalServerError, "Fehler beim Laden")
|
|
return
|
|
}
|
|
if data == nil {
|
|
writeError(w, http.StatusNotFound, "Keine Systemdaten vorhanden")
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, data)
|
|
}
|
|
|
|
// DELETE /api/v1/agents/{id}
|
|
func (h *Handler) deleteAgent(w http.ResponseWriter, r *http.Request) {
|
|
id := r.PathValue("id")
|
|
deleted, err := h.db.DeleteAgent(id)
|
|
if err != nil {
|
|
log.Printf("Fehler beim Loeschen: %v", err)
|
|
writeError(w, http.StatusInternalServerError, "Loeschen fehlgeschlagen")
|
|
return
|
|
}
|
|
if !deleted {
|
|
writeError(w, http.StatusNotFound, "Agent nicht gefunden")
|
|
return
|
|
}
|
|
log.Printf("Agent geloescht: %s", id)
|
|
writeJSON(w, http.StatusOK, map[string]string{"message": "Agent geloescht"})
|
|
}
|
|
|
|
// GET /api/v1/agents/{id}/events
|
|
func (h *Handler) getAgentEvents(w http.ResponseWriter, r *http.Request) {
|
|
agentID := r.PathValue("id")
|
|
eventType := r.URL.Query().Get("type")
|
|
limit := 100
|
|
if l := r.URL.Query().Get("limit"); l != "" {
|
|
if n, err := strconv.Atoi(l); err == nil && n > 0 {
|
|
limit = n
|
|
}
|
|
}
|
|
|
|
events, err := h.db.GetAgentEvents(agentID, eventType, limit)
|
|
if err != nil {
|
|
log.Printf("Fehler beim Laden der Events: %v", err)
|
|
writeError(w, http.StatusInternalServerError, "Fehler beim Laden")
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, events)
|
|
}
|
|
|
|
// GET /api/v1/agents/events
|
|
func (h *Handler) getAllAgentEvents(w http.ResponseWriter, r *http.Request) {
|
|
eventType := r.URL.Query().Get("type")
|
|
limit := 100
|
|
if l := r.URL.Query().Get("limit"); l != "" {
|
|
if n, err := strconv.Atoi(l); err == nil && n > 0 {
|
|
limit = n
|
|
}
|
|
}
|
|
|
|
events, err := h.db.GetAllAgentEvents(eventType, limit)
|
|
if err != nil {
|
|
log.Printf("Fehler beim Laden der Events: %v", err)
|
|
writeError(w, http.StatusInternalServerError, "Fehler beim Laden")
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, events)
|
|
}
|