rmm2/backend/api/customers.go
cynfo3000 15ad9be8e7 feat: Auto Read-only API-Key bei Kunden-Erstellung
- createCustomer erstellt automatisch einen customer-scoped Read-only Key
- Response gibt Customer + neuen API-Key zurueck
- Bestehende 4 Kunden Keys nachtraglich erstellt
2026-03-09 23:56:47 +01:00

171 lines
5.0 KiB
Go

package api
import (
"crypto/rand"
"encoding/hex"
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
"github.com/cynfo/rmm-backend/db"
)
// generateCustomerAPIKey erstellt automatisch einen Read-only Key fuer einen Kunden
func generateCustomerAPIKey(database *db.Database, customerID int, number, name string) (*db.APIKey, error) {
b := make([]byte, 16)
if _, err := rand.Read(b); err != nil {
return nil, err
}
key := hex.EncodeToString(b)
keyName := fmt.Sprintf("Read-only — %s %s", number, name)
return database.CreateAPIKey(keyName, key, "read", &customerID)
}
// GET /api/v1/customers
func (h *Handler) listCustomers(w http.ResponseWriter, r *http.Request) {
customers, err := h.db.GetCustomers()
if err != nil {
writeError(w, http.StatusInternalServerError, "Fehler beim Laden")
return
}
writeJSON(w, http.StatusOK, customers)
}
// POST /api/v1/customers
func (h *Handler) createCustomer(w http.ResponseWriter, r *http.Request) {
var req struct {
Number string `json:"number"`
Name string `json:"name"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.Number == "" || req.Name == "" {
writeError(w, http.StatusBadRequest, "number und name sind erforderlich")
return
}
c, err := h.db.CreateCustomer(req.Number, req.Name)
if err != nil {
writeError(w, http.StatusInternalServerError, "Kunde anlegen fehlgeschlagen")
return
}
// Automatisch einen Read-only API-Key fuer den neuen Kunden erstellen
apiKey, keyErr := generateCustomerAPIKey(h.db, c.ID, req.Number, req.Name)
if keyErr != nil {
log.Printf("Auto-API-Key fuer Kunde %s fehlgeschlagen: %v", req.Number, keyErr)
}
h.auditLog(r, "customer.create", "customer", req.Number, req.Name, "")
resp := map[string]interface{}{
"customer": c,
}
if apiKey != nil {
resp["api_key"] = apiKey
}
writeJSON(w, http.StatusCreated, resp)
}
// GET /api/v1/customers/{id}
func (h *Handler) getCustomer(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.PathValue("id"))
if err != nil {
writeError(w, http.StatusBadRequest, "Ungueltige ID")
return
}
c, err := h.db.GetCustomer(id)
if err != nil {
writeError(w, http.StatusInternalServerError, "Fehler beim Laden")
return
}
if c == nil {
writeError(w, http.StatusNotFound, "Kunde nicht gefunden")
return
}
writeJSON(w, http.StatusOK, c)
}
// PUT /api/v1/customers/{id}
func (h *Handler) updateCustomer(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.PathValue("id"))
if err != nil {
writeError(w, http.StatusBadRequest, "Ungueltige ID")
return
}
var req struct {
Number string `json:"number"`
Name string `json:"name"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.Number == "" || req.Name == "" {
writeError(w, http.StatusBadRequest, "number und name sind erforderlich")
return
}
if err := h.db.UpdateCustomer(id, req.Number, req.Name); err != nil {
writeError(w, http.StatusInternalServerError, "Aktualisierung fehlgeschlagen")
return
}
writeJSON(w, http.StatusOK, map[string]string{"message": "Kunde aktualisiert"})
}
// DELETE /api/v1/customers/{id}
func (h *Handler) deleteCustomer(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.PathValue("id"))
if err != nil {
writeError(w, http.StatusBadRequest, "Ungueltige ID")
return
}
deleted, err := h.db.DeleteCustomer(id)
if err != nil {
writeError(w, http.StatusInternalServerError, "Loeschen fehlgeschlagen")
return
}
if !deleted {
writeError(w, http.StatusNotFound, "Kunde nicht gefunden")
return
}
h.auditLog(r, "customer.delete", "customer", strconv.Itoa(id), "", "")
writeJSON(w, http.StatusOK, map[string]string{"message": "Kunde geloescht"})
}
// GET /api/v1/customers/{id}/agents
func (h *Handler) getCustomerAgents(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.PathValue("id"))
if err != nil {
writeError(w, http.StatusBadRequest, "Ungueltige ID")
return
}
agents, err := h.db.GetAgentsByCustomer(id)
if err != nil {
writeError(w, http.StatusInternalServerError, "Fehler beim Laden")
return
}
writeJSON(w, http.StatusOK, agents)
}
// PUT /api/v1/agents/{id}/customer
func (h *Handler) assignAgentCustomer(w http.ResponseWriter, r *http.Request) {
agentID := r.PathValue("id")
var req struct {
CustomerID int `json:"customer_id"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.CustomerID == 0 {
writeError(w, http.StatusBadRequest, "customer_id erforderlich")
return
}
if err := h.db.AssignAgentCustomer(agentID, req.CustomerID); err != nil {
writeError(w, http.StatusInternalServerError, "Zuordnung fehlgeschlagen")
return
}
writeJSON(w, http.StatusOK, map[string]string{"message": "Agent zugeordnet"})
}
// DELETE /api/v1/agents/{id}/customer
func (h *Handler) unassignAgentCustomer(w http.ResponseWriter, r *http.Request) {
agentID := r.PathValue("id")
if err := h.db.UnassignAgentCustomer(agentID); err != nil {
writeError(w, http.StatusInternalServerError, "Zuordnung entfernen fehlgeschlagen")
return
}
writeJSON(w, http.StatusOK, map[string]string{"message": "Zuordnung entfernt"})
}