fix: IMAP-Konto bearbeiten + Löschen auch bei sync_running

- Store: UpdateCredentials() — Zugangsdaten + Passwort neu verschlüsseln,
  setzt status='idle', error_msg='', sync_running=false zurück
- Handler: PATCH /api/imap/{id} unterstützt nun Credential-Update
  (name/host/username vorhanden = Credential-Update, sonst sync_interval)
- Frontend: "Bearbeiten"-Button öffnet Edit-Dialog mit allen Feldern;
  Passwort-Feld leer = unverändertes Passwort
- Frontend: Löschen-Button nicht mehr durch sync_running blockiert
  (nur noch bei status=running gesperrt)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
sysops
2026-03-20 00:49:37 +01:00
parent 4a4136e4a6
commit c59cad92be
4 changed files with 184 additions and 3 deletions
+39 -2
View File
@@ -273,7 +273,9 @@ func (s *Server) handleSyncNow(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, acc)
}
// handleUpdateImapInterval updates the automatic sync interval for an IMAP account.
// handleUpdateImapInterval updates the automatic sync interval or full credentials
// of an IMAP account. When name/host/username are present the credentials are updated;
// otherwise only sync_interval_min is changed.
func (s *Server) handleUpdateImapInterval(w http.ResponseWriter, r *http.Request) {
if s.imapStore == nil {
writeError(w, http.StatusServiceUnavailable, "IMAP not configured")
@@ -299,13 +301,48 @@ func (s *Server) handleUpdateImapInterval(w http.ResponseWriter, r *http.Request
}
var req struct {
SyncIntervalMin int `json:"sync_interval_min"`
SyncIntervalMin int `json:"sync_interval_min"`
Name string `json:"name"`
Host string `json:"host"`
Port int `json:"port"`
TLS string `json:"tls"`
Username string `json:"username"`
Password string `json:"password"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeError(w, http.StatusBadRequest, "invalid request body")
return
}
// Credential update when name/host/username are provided.
if req.Name != "" || req.Host != "" || req.Username != "" {
if req.Name == "" || req.Host == "" || req.Username == "" {
writeError(w, http.StatusBadRequest, "name, host and username are required for credential update")
return
}
port := req.Port
if port == 0 {
port = acc.Port
}
tls := req.TLS
if tls == "" {
tls = acc.TLS
}
updated := imapstore.Account{Name: req.Name, Host: req.Host, Port: port, TLS: tls, Username: req.Username}
if err := s.imapStore.UpdateCredentials(r.Context(), id, updated, req.Password); err != nil {
writeError(w, http.StatusInternalServerError, "failed to update credentials")
return
}
acc, err = s.imapStore.Get(r.Context(), id)
if err != nil {
writeError(w, http.StatusInternalServerError, "failed to reload account")
return
}
writeJSON(w, http.StatusOK, acc)
return
}
// Sync interval update.
// 0 = disabled; otherwise must be between 5 and 1440 minutes.
if req.SyncIntervalMin != 0 && (req.SyncIntervalMin < 5 || req.SyncIntervalMin > 1440) {
writeError(w, http.StatusBadRequest, "sync_interval_min must be 0 (disabled) or between 5 and 1440")
+27
View File
@@ -282,6 +282,33 @@ func (s *Store) UpdateDone(ctx context.Context, id int64, count int) error {
return nil
}
// UpdateCredentials updates the connection details and optionally the password
// of an IMAP account. Pass an empty password to leave it unchanged.
func (s *Store) UpdateCredentials(ctx context.Context, id int64, acc Account, password string) error {
if password != "" {
enc, err := encryptPassword(password, s.encKey)
if err != nil {
return fmt.Errorf("imap store: encrypt password: %w", err)
}
_, err = s.pool.Exec(ctx,
`UPDATE imap_accounts SET name=$1, host=$2, port=$3, tls=$4, username=$5, password_enc=$6,
status='idle', error_msg='', sync_running=false WHERE id=$7`,
acc.Name, acc.Host, acc.Port, acc.TLS, acc.Username, enc, id)
if err != nil {
return fmt.Errorf("imap store: update credentials: %w", err)
}
} else {
_, err := s.pool.Exec(ctx,
`UPDATE imap_accounts SET name=$1, host=$2, port=$3, tls=$4, username=$5,
status='idle', error_msg='', sync_running=false WHERE id=$6`,
acc.Name, acc.Host, acc.Port, acc.TLS, acc.Username, id)
if err != nil {
return fmt.Errorf("imap store: update credentials (no pw): %w", err)
}
}
return nil
}
// UpdateSyncInterval sets the automatic sync interval for an account.
// intervalMin == 0 disables automatic sync.
func (s *Store) UpdateSyncInterval(ctx context.Context, id int64, intervalMin int) error {