implemented LE and ACME and fixed some bugs

This commit is contained in:
2025-11-27 04:20:09 +01:00
parent ec1e0da9d5
commit 145dfd3d7c
36 changed files with 10583 additions and 1107 deletions

View File

@@ -0,0 +1,182 @@
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)",
})
}