// GET /api/v1/posture — public
func handleGetPosture(w http.ResponseWriter, r *http.Request) {
var row struct {
Posture *string `json:"posture"`
Reason *string `json:"reason"`
UpdatedAt time.Time `json:"updated_at"`
}
err := db.QueryRowContext(ctx,
`SELECT posture, reason, updated_at
FROM deployment_posture WHERE deployment_id = $1`,
deploymentID,
).Scan(&row.Posture, &row.Reason, &row.UpdatedAt)
if err == sql.ErrNoRows {
json.NewEncoder(w).Encode(map[string]any{"posture": nil, "source": "manifest"})
return
}
source := "manifest"
if row.Posture != nil {
source = "operator"
}
json.NewEncoder(w).Encode(map[string]any{
"posture": row.Posture,
"source": source,
"reason": row.Reason,
"updated_at": row.UpdatedAt,
})
}
// POST /api/v1/posture/admin — Cloudflare Access gated at ingress
func handleSetPosture(w http.ResponseWriter, r *http.Request) {
var body struct {
Posture *string `json:"posture"`
Reason string `json:"reason"`
}
json.NewDecoder(r.Body).Decode(&body)
_, err := db.ExecContext(ctx,
`INSERT INTO deployment_posture (deployment_id, posture, reason, updated_at)
VALUES ($1, $2, $3, now())
ON CONFLICT (deployment_id) DO UPDATE
SET posture = $2, reason = $3, updated_at = now()`,
deploymentID, body.Posture, body.Reason,
)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}