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:
@@ -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")
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user