Files
zeekay 4532862f22 fix: use query-param format for crypto bars (sandbox single path 404s)
Same issue as snapshots — Alpaca sandbox returns 404 for
/v1beta3/crypto/us/{sym}/bars but works with
/v1beta3/crypto/us/bars?symbols={sym}. Also handle the wrapped
response format {"bars":{"BTC/USD":[...]}} vs stock {"bars":[...]}.
2025-12-24 00:57:08 -07:00

129 lines
3.1 KiB
Go

package compliance
import (
"encoding/json"
"net/http"
"time"
"github.com/go-chi/chi/v5"
)
// esignHandler holds eSign HTTP handler state.
type esignHandler struct {
store ComplianceStore
}
func (h *esignHandler) handleListEnvelopes(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, h.store.ListEnvelopes())
}
func (h *esignHandler) handleCreateEnvelope(w http.ResponseWriter, r *http.Request) {
var env Envelope
if err := json.NewDecoder(r.Body).Decode(&env); err != nil {
writeError(w, http.StatusBadRequest, "invalid request body")
return
}
if env.Subject == "" {
writeError(w, http.StatusBadRequest, "subject is required")
return
}
if len(env.Signers) == 0 {
writeError(w, http.StatusBadRequest, "at least one signer is required")
return
}
env.Status = EnvelopePending
for i := range env.Signers {
if env.Signers[i].ID == "" {
env.Signers[i].ID = generateID()
}
env.Signers[i].Status = "pending"
}
if err := h.store.SaveEnvelope(&env); err != nil {
writeError(w, http.StatusInternalServerError, err.Error())
return
}
writeJSON(w, http.StatusCreated, &env)
}
func (h *esignHandler) handleGetEnvelope(w http.ResponseWriter, r *http.Request) {
env, err := h.store.GetEnvelope(chi.URLParam(r, "id"))
if err != nil {
writeError(w, http.StatusNotFound, err.Error())
return
}
writeJSON(w, http.StatusOK, env)
}
func (h *esignHandler) handleSign(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id")
env, err := h.store.GetEnvelope(id)
if err != nil {
writeError(w, http.StatusNotFound, err.Error())
return
}
var req struct {
SignerID string `json:"signer_id"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeError(w, http.StatusBadRequest, "invalid request body")
return
}
if req.SignerID == "" {
writeError(w, http.StatusBadRequest, "signer_id is required")
return
}
found := false
allSigned := true
for i := range env.Signers {
if env.Signers[i].ID == req.SignerID {
env.Signers[i].Status = "signed"
env.Signers[i].SignedAt = time.Now().UTC().Format(time.RFC3339)
found = true
}
if env.Signers[i].Status != "signed" {
allSigned = false
}
}
if !found {
writeError(w, http.StatusBadRequest, "signer not found in envelope")
return
}
if allSigned {
env.Status = EnvelopeCompleted
} else {
env.Status = EnvelopeSigned
}
if err := h.store.SaveEnvelope(env); err != nil {
writeError(w, http.StatusInternalServerError, err.Error())
return
}
writeJSON(w, http.StatusOK, env)
}
// --- Templates ---
func (h *esignHandler) handleListTemplates(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, h.store.ListTemplates())
}
func (h *esignHandler) handleCreateTemplate(w http.ResponseWriter, r *http.Request) {
var t Template
if err := json.NewDecoder(r.Body).Decode(&t); err != nil {
writeError(w, http.StatusBadRequest, "invalid request body")
return
}
if t.Name == "" {
writeError(w, http.StatusBadRequest, "name is required")
return
}
if err := h.store.SaveTemplate(&t); err != nil {
writeError(w, http.StatusInternalServerError, err.Error())
return
}
writeJSON(w, http.StatusCreated, &t)
}