fix(docker-watcher): address final review findings

Security:
- Move config export behind auth middleware
- Validate OIDC callback token before storing in localStorage
- Use constant-time comparison for webhook secret
- Encrypt OIDC client secret at rest (like registry tokens)

Performance:
- Make TriggerDeploy async from HTTP handlers (return deploy ID
  immediately, run pipeline in background goroutine)

Robustness:
- Wrap api.ts res.json() in try/catch for non-JSON responses

i18n:
- Replace ~20 hardcoded English validation messages with $t() calls
- Localize ConfirmDialog cancel button, InstanceCard confirm titles,
  ProjectCard instance/instances pluralization
- Add validation keys to both en.json and ru.json
This commit is contained in:
2026-03-28 00:14:53 +03:00
parent a3aa5912d9
commit 1f81ca9eb0
17 changed files with 178 additions and 40 deletions
+13 -1
View File
@@ -10,6 +10,7 @@ import (
"github.com/go-chi/chi/v5"
"github.com/alexei/docker-watcher/internal/auth"
"github.com/alexei/docker-watcher/internal/crypto"
"github.com/alexei/docker-watcher/internal/store"
)
@@ -207,12 +208,23 @@ func (s *Server) updateAuthSettings(w http.ResponseWriter, r *http.Request) {
return
}
// If client secret is masked, preserve the existing value.
// If client secret is masked, preserve the existing encrypted value.
if req.OIDCClientSecret == "********" || req.OIDCClientSecret == "" {
existing, err := s.store.GetAuthSettings()
if err == nil {
req.OIDCClientSecret = existing.OIDCClientSecret
}
} else {
// Encrypt the new client secret before storage.
encrypted, err := crypto.Encrypt(s.encKey, req.OIDCClientSecret)
if err != nil {
respondError(w, http.StatusInternalServerError, "failed to encrypt OIDC client secret")
return
}
// Keep plaintext for OIDC init below, store encrypted.
plaintextSecret := req.OIDCClientSecret
req.OIDCClientSecret = encrypted
defer func() { req.OIDCClientSecret = plaintextSecret }()
}
if err := s.store.UpdateAuthSettings(req); err != nil {