package main import ( "context" "encoding/json" "fmt" "log" "net/http" "time" "github.com/google/uuid" ) // TestCreateRenewalQueueEntryRequest ist die Request-Struktur für das Erstellen eines Test-Queue-Eintrags type TestCreateRenewalQueueEntryRequest struct { CertificateID string `json:"certificateId"` FQDNID string `json:"fqdnId"` SpaceID string `json:"spaceId"` MinutesFromNow int `json:"minutesFromNow"` // Negative Werte = in der Vergangenheit (sofort fällig) } // createTestRenewalQueueEntryHandler erstellt einen Test-Queue-Eintrag für die Renewal-Funktion // Nur für Administratoren zugänglich func createTestRenewalQueueEntryHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS") w.Header().Set("Access-Control-Allow-Headers", "Content-Type") if r.Method == "OPTIONS" { w.WriteHeader(http.StatusOK) return } // Prüfe ob User Admin ist userID, _ := getUserFromRequest(r) if userID == "" { http.Error(w, "Nicht authentifiziert", http.StatusUnauthorized) return } isAdmin, err := isUserAdmin(userID) if err != nil || !isAdmin { http.Error(w, "Nur Administratoren können Test-Queue-Einträge erstellen", http.StatusForbidden) return } // Parse Request Body var req TestCreateRenewalQueueEntryRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Ungültige Request-Daten", http.StatusBadRequest) return } // Validiere Eingaben if req.CertificateID == "" || req.FQDNID == "" || req.SpaceID == "" { http.Error(w, "certificateId, fqdnId und spaceId sind erforderlich", http.StatusBadRequest) return } // Prüfe ob Zertifikat existiert var certExists bool ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() err = db.QueryRowContext(ctx, "SELECT EXISTS(SELECT 1 FROM certificates WHERE id = ?)", req.CertificateID).Scan(&certExists) if err != nil { http.Error(w, "Fehler beim Prüfen des Zertifikats", http.StatusInternalServerError) log.Printf("Fehler beim Prüfen des Zertifikats: %v", err) return } if !certExists { http.Error(w, "Zertifikat nicht gefunden", http.StatusNotFound) return } // Prüfe ob FQDN existiert var fqdnExists bool err = db.QueryRowContext(ctx, "SELECT EXISTS(SELECT 1 FROM fqdns WHERE id = ?)", req.FQDNID).Scan(&fqdnExists) if err != nil { http.Error(w, "Fehler beim Prüfen des FQDN", http.StatusInternalServerError) log.Printf("Fehler beim Prüfen des FQDN: %v", err) return } if !fqdnExists { http.Error(w, "FQDN nicht gefunden", http.StatusNotFound) return } // Berechne scheduled_at Zeitpunkt now := time.Now().UTC() scheduledAt := now.Add(time.Duration(req.MinutesFromNow) * time.Minute) scheduledAtStr := scheduledAt.Format("2006-01-02 15:04:05") // Generiere eindeutige Queue-ID queueID := fmt.Sprintf("test-%s", uuid.New().String()) createdAt := now.Format("2006-01-02 15:04:05") // Erstelle Queue-Eintrag ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second) defer cancel() _, err = db.ExecContext(ctx, ` INSERT INTO renewal_queue (id, certificate_id, fqdn_id, space_id, scheduled_at, status, created_at) VALUES (?, ?, ?, ?, ?, 'pending', ?) `, queueID, req.CertificateID, req.FQDNID, req.SpaceID, scheduledAtStr, createdAt) if err != nil { http.Error(w, "Fehler beim Erstellen des Queue-Eintrags", http.StatusInternalServerError) log.Printf("Fehler beim Erstellen des Queue-Eintrags: %v", err) return } // Lade erstellten Eintrag var entry struct { ID string `json:"id"` CertificateID string `json:"certificateId"` FQDNID string `json:"fqdnId"` SpaceID string `json:"spaceId"` ScheduledAt string `json:"scheduledAt"` Status string `json:"status"` CreatedAt string `json:"createdAt"` } err = db.QueryRowContext(ctx, ` SELECT id, certificate_id, fqdn_id, space_id, scheduled_at, status, created_at FROM renewal_queue WHERE id = ? `, queueID).Scan(&entry.ID, &entry.CertificateID, &entry.FQDNID, &entry.SpaceID, &entry.ScheduledAt, &entry.Status, &entry.CreatedAt) if err != nil { http.Error(w, "Fehler beim Abrufen des erstellten Eintrags", http.StatusInternalServerError) log.Printf("Fehler beim Abrufen des erstellten Eintrags: %v", err) return } w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(map[string]interface{}{ "success": true, "entry": entry, "message": fmt.Sprintf("Test-Queue-Eintrag erstellt (geplant: %s)", scheduledAtStr), }) } // triggerRenewalQueueHandler führt die Queue-Verarbeitung manuell aus // Nur für Administratoren zugänglich func triggerRenewalQueueHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS") w.Header().Set("Access-Control-Allow-Headers", "Content-Type") if r.Method == "OPTIONS" { w.WriteHeader(http.StatusOK) return } // Prüfe ob User Admin ist userID, _ := getUserFromRequest(r) if userID == "" { http.Error(w, "Nicht authentifiziert", http.StatusUnauthorized) return } isAdmin, err := isUserAdmin(userID) if err != nil || !isAdmin { http.Error(w, "Nur Administratoren können die Queue-Verarbeitung manuell auslösen", http.StatusForbidden) return } // Führe Queue-Verarbeitung in einer Goroutine aus, um den Request nicht zu blockieren go func() { log.Println("Manuelle Queue-Verarbeitung gestartet (via Test-Endpoint)") processRenewalQueue() log.Println("Manuelle Queue-Verarbeitung abgeschlossen") }() w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(map[string]interface{}{ "success": true, "message": "Queue-Verarbeitung wurde gestartet (läuft im Hintergrund)", }) }