package main import ( "context" "crypto/subtle" "encoding/json" "errors" "net/http" "strings" "git.cin.su/m/Assistant/adapters/postgres" "git.cin.su/m/Assistant/contracts/continuity" continuitydomain "git.cin.su/m/Assistant/modules/continuity/domain" ) type continuityExecutorRepository interface { SatisfyDelivered(context.Context, continuity.SatisfyDeliveredCommand) (continuity.ActionReceipt, error) LoadActionReceipt(context.Context, continuity.CommittedActionIntentID) (continuity.ActionReceipt, error) } func registerInternalExecutorRoutes(mux *http.ServeMux, repository continuityExecutorRepository, token string) { mux.HandleFunc("POST /v1/internal/executor/satisfy-delivered", requireExecutorToken(token, func(w http.ResponseWriter, r *http.Request) { var command continuity.SatisfyDeliveredCommand decoder := json.NewDecoder(http.MaxBytesReader(w, r.Body, 32<<10)) if err := decoder.Decode(&command); err != nil { http.Error(w, "invalid executor command", http.StatusBadRequest) return } receipt, err := repository.SatisfyDelivered(r.Context(), command) switch { case err == nil: w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(receipt) case errors.Is(err, continuitydomain.ErrInvalidSatisfaction): http.Error(w, "invalid executor command", http.StatusBadRequest) case errors.Is(err, postgres.ErrProspectiveNotFound): http.Error(w, "prospective item not found", http.StatusNotFound) case errors.Is(err, postgres.ErrProspectiveOwnerConflict), errors.Is(err, postgres.ErrProspectiveStateConflict), errors.Is(err, postgres.ErrProspectiveRevisionConflict), errors.Is(err, postgres.ErrActionIntentConflict): http.Error(w, "prospective command conflict", http.StatusConflict) default: http.Error(w, "continuity execution unavailable", http.StatusInternalServerError) } })) mux.HandleFunc("GET /v1/internal/executor/receipts/{intentID}", requireExecutorToken(token, func(w http.ResponseWriter, r *http.Request) { receipt, err := repository.LoadActionReceipt(r.Context(), continuity.CommittedActionIntentID(r.PathValue("intentID"))) if errors.Is(err, postgres.ErrActionReceiptNotFound) { http.Error(w, "receipt not found", http.StatusNotFound) return } if err != nil { http.Error(w, "continuity receipt unavailable", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(receipt) })) } func requireExecutorToken(expected string, next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { provided := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ") if expected == "" || len(provided) != len(expected) || subtle.ConstantTimeCompare([]byte(provided), []byte(expected)) != 1 { http.Error(w, "executor denied", http.StatusUnauthorized) return } next(w, r) } }