Make delivery and integration failures explicit
Persist reminder presentations and retry state, atomically complete collapsed deliveries, fall back across away reaches, and block permanent failures visibly (V-715, V-678). Fail closed when enabled integrations lack credentials and keep remote arms explicitly dark (V-691). Give mavweb one sanitized, request-correlated error contract (V-689). Owner explicitly requested direct commits to master.
This commit is contained in:
+18
-12
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
@@ -32,16 +33,18 @@ var presenceSignals = map[string]string{
|
||||
// is a marker. Only allowlisted keys are accepted (see presenceSignals).
|
||||
func handleSignal(w http.ResponseWriter, r *http.Request, core ipc.CoreAPI) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "POST only", http.StatusMethodNotAllowed)
|
||||
writeProblem(w, r, http.StatusMethodNotAllowed, problemMethodNotAllowed,
|
||||
"POST only", nil)
|
||||
return
|
||||
}
|
||||
if !requireCore(w, core, "presence ingest") {
|
||||
if !requireCore(w, r, core, "presence ingest") {
|
||||
return
|
||||
}
|
||||
key := r.URL.Query().Get("key")
|
||||
source, ok := presenceSignals[key]
|
||||
if !ok {
|
||||
http.Error(w, "unknown signal key", http.StatusBadRequest)
|
||||
writeProblem(w, r, http.StatusBadRequest, problemInvalidRequest,
|
||||
"unknown signal key", nil)
|
||||
return
|
||||
}
|
||||
// kind=env: an observation about the device/surface, NOT a self-fact — a
|
||||
@@ -56,8 +59,8 @@ func handleSignal(w http.ResponseWriter, r *http.Request, core ipc.CoreAPI) {
|
||||
Source: source,
|
||||
Confidence: 1.0,
|
||||
}); err != nil {
|
||||
log.Printf("signal %s: %v", key, err)
|
||||
http.Error(w, "write failed", http.StatusBadGateway)
|
||||
writeProblem(w, r, http.StatusBadGateway, problemCoreWriteFailed,
|
||||
"write failed", fmt.Errorf("write presence signal %q: %w", key, err))
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
@@ -65,29 +68,32 @@ func handleSignal(w http.ResponseWriter, r *http.Request, core ipc.CoreAPI) {
|
||||
|
||||
func handleRevert(w http.ResponseWriter, r *http.Request, core ipc.CoreAPI, session *webauthn.PasskeySession, requireStepUp bool) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "POST only", http.StatusMethodNotAllowed)
|
||||
writeProblem(w, r, http.StatusMethodNotAllowed, problemMethodNotAllowed,
|
||||
"POST only", nil)
|
||||
return
|
||||
}
|
||||
if !requireCore(w, core, "revert") {
|
||||
if !requireCore(w, r, core, "revert") {
|
||||
return
|
||||
}
|
||||
if !stepUpGate(w, session, requireStepUp) {
|
||||
if !stepUpGate(w, r, session, requireStepUp) {
|
||||
return
|
||||
}
|
||||
|
||||
key := strings.TrimSpace(r.FormValue("key"))
|
||||
if key == "" {
|
||||
http.Error(w, "key required", http.StatusBadRequest)
|
||||
writeProblem(w, r, http.StatusBadRequest, problemInvalidRequest,
|
||||
"key required", nil)
|
||||
return
|
||||
}
|
||||
newID, err := core.RevertFact(r.Context(), key)
|
||||
if err != nil {
|
||||
log.Printf("revert %q: %v", key, err)
|
||||
if errors.Is(err, ipc.ErrNoFact) {
|
||||
http.Error(w, "no fact to revert", http.StatusNotFound)
|
||||
writeProblem(w, r, http.StatusNotFound, problemResourceNotFound,
|
||||
"no fact to revert", fmt.Errorf("revert fact %q: %w", key, err))
|
||||
return
|
||||
}
|
||||
http.Error(w, "revert failed", http.StatusBadGateway)
|
||||
writeProblem(w, r, http.StatusBadGateway, problemCoreChangeFailed,
|
||||
"revert failed", fmt.Errorf("revert fact %q: %w", key, err))
|
||||
return
|
||||
}
|
||||
log.Printf("reverted fact for key=%s, new_id=%d", key, newID)
|
||||
|
||||
Reference in New Issue
Block a user