implemented LE and ACME and fixed some bugs
This commit is contained in:
267
backend/renewal_queue_handlers.go
Normal file
267
backend/renewal_queue_handlers.go
Normal file
@@ -0,0 +1,267 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// updateFqdnRenewalEnabledHandler aktualisiert das renewal_enabled Flag für einen FQDN
|
||||
func updateFqdnRenewalEnabledHandler(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", "PUT, OPTIONS")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
|
||||
|
||||
if r.Method == "OPTIONS" {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
spaceID := vars["spaceId"]
|
||||
fqdnID := vars["fqdnId"]
|
||||
|
||||
// Prüfe Berechtigung
|
||||
userID, _ := getUserFromRequest(r)
|
||||
if userID == "" {
|
||||
http.Error(w, "Nicht authentifiziert", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
hasAccess, err := hasSpaceAccess(userID, spaceID)
|
||||
if err != nil {
|
||||
http.Error(w, "Fehler beim Prüfen der Berechtigung", http.StatusInternalServerError)
|
||||
log.Printf("Fehler beim Prüfen der Berechtigung: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !hasAccess {
|
||||
http.Error(w, "Keine Berechtigung für diesen Space", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
// Prüfe ob FQDN existiert und hole aktuellen renewal_enabled Status
|
||||
var currentRenewalEnabled sql.NullInt64
|
||||
err = db.QueryRow("SELECT renewal_enabled FROM fqdns WHERE id = ? AND space_id = ?", fqdnID, spaceID).Scan(¤tRenewalEnabled)
|
||||
if err == sql.ErrNoRows {
|
||||
http.Error(w, "FQDN nicht gefunden", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
http.Error(w, "Fehler beim Laden des FQDN", http.StatusInternalServerError)
|
||||
log.Printf("Fehler beim Laden des FQDN: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse Request Body
|
||||
var req struct {
|
||||
RenewalEnabled bool `json:"renewalEnabled"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "Ungültige Anfrage", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Prüfe ob renewal_enabled von false auf true geändert wird
|
||||
wasDisabled := !currentRenewalEnabled.Valid || currentRenewalEnabled.Int64 == 0
|
||||
willBeEnabled := req.RenewalEnabled
|
||||
shouldProcessRenewalInfo := wasDisabled && willBeEnabled
|
||||
|
||||
// Beginne Transaktion
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
tx, err := db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
http.Error(w, "Fehler beim Starten der Transaktion", http.StatusInternalServerError)
|
||||
log.Printf("Fehler beim Starten der Transaktion: %v", err)
|
||||
return
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
// Update renewal_enabled (explizit als 0 oder 1 speichern, nicht NULL)
|
||||
var renewalEnabledInt int
|
||||
if req.RenewalEnabled {
|
||||
renewalEnabledInt = 1
|
||||
} else {
|
||||
renewalEnabledInt = 0
|
||||
}
|
||||
|
||||
_, err = tx.ExecContext(ctx, "UPDATE fqdns SET renewal_enabled = ? WHERE id = ? AND space_id = ?", renewalEnabledInt, fqdnID, spaceID)
|
||||
if err != nil {
|
||||
http.Error(w, "Fehler beim Aktualisieren des renewal_enabled Flags", http.StatusInternalServerError)
|
||||
log.Printf("Fehler beim Aktualisieren des renewal_enabled Flags: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Wenn renewal_enabled deaktiviert wird, lösche alle Queue-Einträge für diesen FQDN
|
||||
if !req.RenewalEnabled {
|
||||
_, err = tx.ExecContext(ctx, "DELETE FROM renewal_queue WHERE fqdn_id = ?", fqdnID)
|
||||
if err != nil {
|
||||
http.Error(w, "Fehler beim Löschen der Queue-Einträge", http.StatusInternalServerError)
|
||||
log.Printf("Fehler beim Löschen der Queue-Einträge: %v", err)
|
||||
return
|
||||
}
|
||||
log.Printf("Queue-Einträge für FQDN %s gelöscht (renewal_enabled deaktiviert)", fqdnID)
|
||||
}
|
||||
|
||||
// Committe die Transaktion
|
||||
if err = tx.Commit(); err != nil {
|
||||
http.Error(w, "Fehler beim Speichern der Änderungen", http.StatusInternalServerError)
|
||||
log.Printf("Fehler beim Committen der Transaktion: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Wenn renewal_enabled von false auf true geändert wurde, verarbeite RenewalInfo für das aktuelle Zertifikat
|
||||
if shouldProcessRenewalInfo {
|
||||
go func() {
|
||||
// Hole das aktuellste (nicht-intermediate) Zertifikat für diesen FQDN
|
||||
var certID, certPEM string
|
||||
err := db.QueryRow(`
|
||||
SELECT id, certificate_pem
|
||||
FROM certificates
|
||||
WHERE fqdn_id = ? AND (is_intermediate = 0 OR is_intermediate IS NULL)
|
||||
ORDER BY expires_at DESC, created_at DESC
|
||||
LIMIT 1
|
||||
`, fqdnID).Scan(&certID, &certPEM)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
log.Printf("Kein Zertifikat für FQDN %s gefunden - RenewalInfo wird übersprungen", fqdnID)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("Fehler beim Laden des Zertifikats für FQDN %s: %v", fqdnID, err)
|
||||
return
|
||||
}
|
||||
|
||||
if certPEM == "" {
|
||||
log.Printf("Zertifikat %s hat kein PEM - RenewalInfo wird übersprungen", certID)
|
||||
return
|
||||
}
|
||||
|
||||
// Verarbeite RenewalInfo im Hintergrund
|
||||
log.Printf("Verarbeite RenewalInfo für Zertifikat %s (FQDN %s wurde aktiviert)", certID, fqdnID)
|
||||
if err := ProcessRenewalInfoForCertificate(certPEM, certID, fqdnID, spaceID, true); err != nil {
|
||||
log.Printf("Fehler beim Verarbeiten der RenewalInfo für FQDN %s (wird ignoriert): %v", fqdnID, err)
|
||||
} else {
|
||||
log.Printf("RenewalInfo erfolgreich verarbeitet für FQDN %s", fqdnID)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Hole Username für Audit-Log
|
||||
userID, username := getUserFromRequest(r)
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"success": true,
|
||||
"renewalEnabled": req.RenewalEnabled,
|
||||
})
|
||||
|
||||
// Audit-Log
|
||||
if auditService != nil {
|
||||
ipAddress, userAgent := getRequestInfo(r)
|
||||
auditService.Track(r.Context(), "UPDATE", "fqdn", fqdnID, userID, username, map[string]interface{}{
|
||||
"spaceId": spaceID,
|
||||
"renewalEnabled": req.RenewalEnabled,
|
||||
"message": fmt.Sprintf("Renewal-Status für FQDN aktualisiert: %v", req.RenewalEnabled),
|
||||
}, ipAddress, userAgent)
|
||||
}
|
||||
}
|
||||
|
||||
// getRenewalQueueHandler gibt alle Einträge aus der renewal_queue zurück
|
||||
func getRenewalQueueHandler(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", "GET, OPTIONS")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
|
||||
|
||||
if r.Method == "OPTIONS" {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
// Prüfe Berechtigung - jeder authentifizierte User kann die Queue sehen
|
||||
userID, _ := getUserFromRequest(r)
|
||||
if userID == "" {
|
||||
http.Error(w, "Nicht authentifiziert", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// Hole alle Queue-Einträge mit FQDN und Space-Informationen
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
rows, err := db.QueryContext(ctx, `
|
||||
SELECT
|
||||
rq.id,
|
||||
rq.certificate_id,
|
||||
rq.fqdn_id,
|
||||
rq.space_id,
|
||||
rq.scheduled_at,
|
||||
rq.status,
|
||||
rq.created_at,
|
||||
rq.processed_at,
|
||||
rq.error_message,
|
||||
f.fqdn,
|
||||
s.name as space_name
|
||||
FROM renewal_queue rq
|
||||
LEFT JOIN fqdns f ON rq.fqdn_id = f.id
|
||||
LEFT JOIN spaces s ON rq.space_id = s.id
|
||||
ORDER BY rq.scheduled_at ASC
|
||||
`)
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, "Fehler beim Laden der Renewal Queue", http.StatusInternalServerError)
|
||||
log.Printf("Fehler beim Laden der Renewal Queue: %v", err)
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var queueItems []map[string]interface{}
|
||||
for rows.Next() {
|
||||
var id, certID, fqdnID, spaceID, scheduledAt, status, createdAt, fqdn, spaceName string
|
||||
var processedAt, errorMessage sql.NullString
|
||||
|
||||
err := rows.Scan(&id, &certID, &fqdnID, &spaceID, &scheduledAt, &status, &createdAt, &processedAt, &errorMessage, &fqdn, &spaceName)
|
||||
if err != nil {
|
||||
log.Printf("Fehler beim Scannen der Queue-Zeile: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
item := map[string]interface{}{
|
||||
"id": id,
|
||||
"certificateId": certID,
|
||||
"fqdnId": fqdnID,
|
||||
"spaceId": spaceID,
|
||||
"scheduledAt": scheduledAt,
|
||||
"status": status,
|
||||
"createdAt": createdAt,
|
||||
"fqdn": fqdn,
|
||||
"spaceName": spaceName,
|
||||
}
|
||||
|
||||
if processedAt.Valid {
|
||||
item["processedAt"] = processedAt.String
|
||||
}
|
||||
if errorMessage.Valid {
|
||||
item["errorMessage"] = errorMessage.String
|
||||
}
|
||||
|
||||
queueItems = append(queueItems, item)
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"success": true,
|
||||
"queue": queueItems,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user