Adds the composable monorepo template system that generates project skeletons with pluggable components (service, worker, app-react, app-astro, cli). Key changes: - Monorepo skeleton templates with shared pkg/, scripts/, and git hooks - Component templates (service, worker, app-react, app-astro, cli) with Dockerfiles, CI steps, and component.yaml manifests - Component domain model with validation and dependency resolution - Component handler endpoints for CRUD and composition - Template provider extended with BuildComposableProject and component assembly - Deployer extended with composable project deployment support - Handler timeout constants (TimeoutFastLookup through TimeoutLongRunning) - envutil package for centralized env var reads with defaults - api.DecodeJSON helper for standardized request body decoding - Standardized response helpers (WriteBadRequest, WriteNotFound, etc.) - Replaced fullstack-app cookbook with composable-app cookbook - Hardened handler timeouts, logging, and error responses across all handlers Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
76 lines
1.9 KiB
Cheetah
76 lines
1.9 KiB
Cheetah
// Package httpresponse provides standard HTTP response types and helpers.
|
|
//
|
|
// This package implements an envelope pattern for consistent API responses:
|
|
//
|
|
// {
|
|
// "data": {...}, // Present on success
|
|
// "error": {...}, // Present on error
|
|
// "meta": {
|
|
// "request_id": "...",
|
|
// "trace_id": "...",
|
|
// "timestamp": "..."
|
|
// }
|
|
// }
|
|
//
|
|
// Usage:
|
|
//
|
|
// func GetUser(w http.ResponseWriter, r *http.Request) {
|
|
// user, err := svc.Get(ctx, id)
|
|
// if err != nil {
|
|
// httpresponse.NotFound(w, r, "user not found")
|
|
// return
|
|
// }
|
|
// httpresponse.OK(w, r, user)
|
|
// }
|
|
package httpresponse
|
|
|
|
import (
|
|
"net/http"
|
|
"time"
|
|
|
|
"{{GO_MODULE}}/pkg/httpcontext"
|
|
)
|
|
|
|
// Response is the standard envelope for all API responses.
|
|
type Response struct {
|
|
Data any `json:"data,omitempty"`
|
|
Error *Error `json:"error,omitempty"`
|
|
Meta Meta `json:"meta"`
|
|
}
|
|
|
|
// Error represents an API error in the response envelope.
|
|
type Error struct {
|
|
Code string `json:"code"`
|
|
Message string `json:"message"`
|
|
Details any `json:"details,omitempty"`
|
|
}
|
|
|
|
// Meta contains response metadata.
|
|
type Meta struct {
|
|
RequestID string `json:"request_id,omitempty"`
|
|
TraceID string `json:"trace_id,omitempty"`
|
|
Timestamp string `json:"timestamp"`
|
|
}
|
|
|
|
// newMeta creates a Meta with current timestamp, request ID, and trace ID from context.
|
|
func newMeta(r *http.Request) Meta {
|
|
requestID, _ := httpcontext.GetRequestID(r.Context())
|
|
traceID, _ := httpcontext.GetTraceID(r.Context())
|
|
return Meta{
|
|
RequestID: requestID,
|
|
TraceID: traceID,
|
|
Timestamp: time.Now().UTC().Format(time.RFC3339),
|
|
}
|
|
}
|
|
|
|
// Error codes for machine-readable error classification.
|
|
const (
|
|
CodeBadRequest = "BAD_REQUEST"
|
|
CodeUnauthorized = "UNAUTHORIZED"
|
|
CodeForbidden = "FORBIDDEN"
|
|
CodeNotFound = "NOT_FOUND"
|
|
CodeConflict = "CONFLICT"
|
|
CodeInternal = "INTERNAL_ERROR"
|
|
CodeValidation = "VALIDATION_ERROR"
|
|
)
|