fix: ConfirmDialog accessibility and standardize destructive action confirmations
- Add focus trap, Escape key handling, ARIA attributes to ConfirmDialog - Replace native confirm() with ConfirmDialog for stage, registry, user deletion - Add confirmation dialogs for env variable and volume deletion
This commit is contained in:
+101
-1
@@ -1,12 +1,15 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/alexei/docker-watcher/internal/crypto"
|
||||
"github.com/alexei/docker-watcher/internal/npm"
|
||||
"github.com/alexei/docker-watcher/internal/store"
|
||||
"github.com/alexei/docker-watcher/internal/webhook"
|
||||
)
|
||||
|
||||
@@ -93,14 +96,22 @@ func (s *Server) updateSettings(w http.ResponseWriter, r *http.Request) {
|
||||
if req.PollingInterval != "" {
|
||||
updated.PollingInterval = req.PollingInterval
|
||||
}
|
||||
if req.SSLCertificateID != nil {
|
||||
sslChanged := false
|
||||
if req.SSLCertificateID != nil && *req.SSLCertificateID != updated.SSLCertificateID {
|
||||
updated.SSLCertificateID = *req.SSLCertificateID
|
||||
sslChanged = true
|
||||
}
|
||||
|
||||
if err := s.store.UpdateSettings(updated); err != nil {
|
||||
respondError(w, http.StatusInternalServerError, "failed to update settings: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// If SSL cert changed, update all existing NPM proxy hosts in the background.
|
||||
if sslChanged {
|
||||
go s.reapplySSLToAllProxies(updated)
|
||||
}
|
||||
|
||||
respondJSON(w, http.StatusOK, map[string]string{"status": "updated"})
|
||||
}
|
||||
|
||||
@@ -204,3 +215,92 @@ func isWildcardCert(cert npm.Certificate) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// reapplySSLToAllProxies updates all existing NPM proxy hosts managed by Docker Watcher
|
||||
// to use the new SSL certificate. Runs in the background after settings change.
|
||||
func (s *Server) reapplySSLToAllProxies(settings store.Settings) {
|
||||
ctx := context.Background()
|
||||
|
||||
npmPassword, err := crypto.Decrypt(s.encKey, settings.NpmPassword)
|
||||
if err != nil {
|
||||
slog.Error("reapply SSL: decrypt npm password", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
npmClient := npm.New(settings.NpmURL)
|
||||
if err := npmClient.Authenticate(ctx, settings.NpmEmail, npmPassword); err != nil {
|
||||
slog.Error("reapply SSL: authenticate to NPM", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Get all proxy hosts from NPM.
|
||||
hosts, err := npmClient.ListProxyHosts(ctx)
|
||||
if err != nil {
|
||||
slog.Error("reapply SSL: list proxy hosts", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Get all our managed instances to identify which proxy hosts are ours.
|
||||
projects, err := s.store.GetAllProjects()
|
||||
if err != nil {
|
||||
slog.Error("reapply SSL: get projects", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Build a set of NPM proxy IDs that belong to our instances.
|
||||
managedProxyIDs := make(map[int]bool)
|
||||
for _, p := range projects {
|
||||
stages, err := s.store.GetStagesByProjectID(p.ID)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, st := range stages {
|
||||
instances, err := s.store.GetInstancesByStageID(st.ID)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, inst := range instances {
|
||||
if inst.NpmProxyID > 0 {
|
||||
managedProxyIDs[inst.NpmProxyID] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updated := 0
|
||||
for _, host := range hosts {
|
||||
if !managedProxyIDs[host.ID] {
|
||||
continue
|
||||
}
|
||||
|
||||
config := npm.ProxyHostConfig{
|
||||
DomainNames: host.DomainNames,
|
||||
ForwardScheme: host.ForwardScheme,
|
||||
ForwardHost: host.ForwardHost,
|
||||
ForwardPort: host.ForwardPort,
|
||||
BlockExploits: true,
|
||||
AllowWebsocket: true,
|
||||
HTTP2Support: true,
|
||||
Meta: npm.Meta{},
|
||||
Locations: []any{},
|
||||
}
|
||||
|
||||
if settings.SSLCertificateID > 0 {
|
||||
config.CertificateID = settings.SSLCertificateID
|
||||
config.SSLForced = true
|
||||
config.HSTSEnabled = true
|
||||
} else {
|
||||
config.CertificateID = 0
|
||||
config.SSLForced = false
|
||||
config.HSTSEnabled = false
|
||||
}
|
||||
|
||||
if _, err := npmClient.UpdateProxyHost(ctx, host.ID, config); err != nil {
|
||||
slog.Warn("reapply SSL: update proxy host failed", "host_id", host.ID, "error", err)
|
||||
continue
|
||||
}
|
||||
updated++
|
||||
}
|
||||
|
||||
slog.Info("reapply SSL: completed", "updated", updated, "total_managed", len(managedProxyIDs))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user