fix: harden operations and configuration recovery

This commit is contained in:
proxyctl coordinator
2026-08-30 11:13:45 +08:00
parent e34bf7d989
commit 8928986830
27 changed files with 542 additions and 125 deletions
+18 -3
View File
@@ -1,5 +1,12 @@
package app
import (
"context"
"time"
)
const operationTimeout = 5 * time.Minute
// StatusKind gives presentation layers semantics without requiring them to
// inspect user-facing status text.
type StatusKind uint8
@@ -27,6 +34,9 @@ type Confirmation struct {
ConfirmLabel string
CancelLabel string
HighImpact bool
// Token binds acceptance to the exact state and operation scope that was
// presented. Presentation layers must return it with the confirmed Intent.
Token string
}
// TargetProgressPhase describes one Target's part in an operation stream.
@@ -189,6 +199,7 @@ func (u OperationUpdate) IsTerminal() bool { return u.Decode().IsTerm
type OperationStream struct {
Snapshot Snapshot
Updates <-chan OperationUpdate
Cancel context.CancelFunc
}
// InteractiveModule is the presentation-independent Application seam used by
@@ -216,16 +227,20 @@ func (a *Application) StartOperation(intent Intent) OperationStream {
a.interactiveOperation = true
a.interactiveMu.Unlock()
ctx, cancel := context.WithTimeout(context.Background(), operationTimeout)
updates := make(chan OperationUpdate, 2*len(a.targets)+2)
go func() {
defer close(updates)
defer a.finishInteractiveOperation()
final := a.dispatch(intent, func(result Result) {
defer cancel()
final := a.dispatch(ctx, intent, func(result Result) {
updates <- runningOperationUpdate(result)
})
// Release the Application gate before publishing the terminal update so
// an immediate confirmation retry cannot observe a stale busy state.
a.finishInteractiveOperation()
updates <- terminalOperationUpdate(final)
}()
return OperationStream{Snapshot: initial, Updates: updates}
return OperationStream{Snapshot: initial, Updates: updates, Cancel: cancel}
}
func (a *Application) finishInteractiveOperation() {