|
- package handlers
-
- import (
- "encoding/json"
- "net/http"
- "strconv"
- "strings"
-
- "github.com/go-chi/chi/v5"
-
- "qctextbuilder/internal/buildsvc"
- "qctextbuilder/internal/draftsvc"
- "qctextbuilder/internal/onboarding"
- "qctextbuilder/internal/templatesvc"
- )
-
- type API struct {
- templateSvc *templatesvc.Service
- onboardSvc *onboarding.Service
- draftSvc *draftsvc.Service
- buildSvc buildsvc.Service
- }
-
- func NewAPI(templateSvc *templatesvc.Service, onboardSvc *onboarding.Service, draftSvc *draftsvc.Service, buildSvc buildsvc.Service) *API {
- return &API{
- templateSvc: templateSvc,
- onboardSvc: onboardSvc,
- draftSvc: draftSvc,
- buildSvc: buildSvc,
- }
- }
-
- func (a *API) Health(w http.ResponseWriter, _ *http.Request) {
- writeJSON(w, http.StatusOK, map[string]any{"status": "ok"})
- }
-
- func (a *API) SyncTemplates(w http.ResponseWriter, r *http.Request) {
- templates, err := a.templateSvc.SyncAITemplates(r.Context())
- if err != nil {
- writeJSON(w, http.StatusBadGateway, map[string]any{"error": err.Error()})
- return
- }
- writeJSON(w, http.StatusOK, map[string]any{"count": len(templates), "templates": templates})
- }
-
- func (a *API) ListTemplates(w http.ResponseWriter, r *http.Request) {
- templates, err := a.templateSvc.ListTemplates(r.Context())
- if err != nil {
- writeJSON(w, http.StatusBadRequest, map[string]any{"error": err.Error()})
- return
- }
- writeJSON(w, http.StatusOK, map[string]any{"count": len(templates), "templates": templates})
- }
-
- func (a *API) GetTemplateDetail(w http.ResponseWriter, r *http.Request) {
- rawID := chi.URLParam(r, "id")
- templateID, err := strconv.ParseInt(rawID, 10, 64)
- if err != nil {
- writeJSON(w, http.StatusBadRequest, map[string]any{"error": "invalid template id"})
- return
- }
-
- detail, err := a.templateSvc.GetTemplateDetail(r.Context(), templateID)
- if err != nil {
- writeJSON(w, http.StatusNotFound, map[string]any{"error": err.Error()})
- return
- }
- writeJSON(w, http.StatusOK, detail)
- }
-
- func (a *API) OnboardTemplate(w http.ResponseWriter, r *http.Request) {
- rawID := chi.URLParam(r, "id")
- templateID, err := strconv.ParseInt(rawID, 10, 64)
- if err != nil {
- writeJSON(w, http.StatusBadRequest, map[string]any{"error": "invalid template id"})
- return
- }
-
- manifest, fields, err := a.onboardSvc.OnboardTemplate(r.Context(), templateID)
- if err != nil {
- writeJSON(w, http.StatusBadRequest, map[string]any{"error": err.Error()})
- return
- }
- writeJSON(w, http.StatusOK, map[string]any{
- "manifestId": manifest.ID,
- "fieldCount": len(fields),
- "status": "reviewed",
- })
- }
-
- type updateTemplateFieldsRequest struct {
- ManifestID string `json:"manifestId"`
- Fields []updateTemplateFieldItem `json:"fields"`
- }
-
- type updateTemplateFieldItem struct {
- Path string `json:"path"`
- IsEnabled *bool `json:"isEnabled,omitempty"`
- IsRequiredByUs *bool `json:"isRequiredByUs,omitempty"`
- DisplayLabel *string `json:"displayLabel,omitempty"`
- DisplayOrder *int `json:"displayOrder,omitempty"`
- WebsiteSection *string `json:"websiteSection,omitempty"`
- Notes *string `json:"notes,omitempty"`
- }
-
- func (a *API) UpdateTemplateFields(w http.ResponseWriter, r *http.Request) {
- rawID := chi.URLParam(r, "id")
- templateID, err := strconv.ParseInt(rawID, 10, 64)
- if err != nil {
- writeJSON(w, http.StatusBadRequest, map[string]any{"error": "invalid template id"})
- return
- }
-
- var req updateTemplateFieldsRequest
- if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
- writeJSON(w, http.StatusBadRequest, map[string]any{"error": "invalid JSON body"})
- return
- }
- if len(req.Fields) == 0 {
- writeJSON(w, http.StatusBadRequest, map[string]any{"error": "fields is required"})
- return
- }
-
- patches := make([]onboarding.FieldPatch, 0, len(req.Fields))
- for _, f := range req.Fields {
- patches = append(patches, onboarding.FieldPatch{
- Path: f.Path,
- IsEnabled: f.IsEnabled,
- IsRequiredByUs: f.IsRequiredByUs,
- DisplayLabel: f.DisplayLabel,
- DisplayOrder: f.DisplayOrder,
- WebsiteSection: f.WebsiteSection,
- Notes: f.Notes,
- })
- }
-
- manifest, fields, err := a.onboardSvc.UpdateTemplateFields(r.Context(), templateID, req.ManifestID, patches)
- if err != nil {
- writeJSON(w, http.StatusBadRequest, map[string]any{"error": err.Error()})
- return
- }
-
- writeJSON(w, http.StatusOK, map[string]any{
- "templateId": templateID,
- "manifestId": manifest.ID,
- "fieldCount": len(fields),
- "fields": fields,
- })
- }
-
- func (a *API) StartBuild(w http.ResponseWriter, r *http.Request) {
- var req buildsvc.StartBuildRequest
- if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
- writeJSON(w, http.StatusBadRequest, map[string]any{"error": "invalid JSON body"})
- return
- }
-
- result, err := a.buildSvc.StartBuild(r.Context(), req)
- if err != nil {
- writeJSON(w, http.StatusBadRequest, map[string]any{"error": err.Error()})
- return
- }
- writeJSON(w, http.StatusAccepted, result)
- }
-
- type upsertDraftRequest struct {
- TemplateID int64 `json:"templateId"`
- ManifestID string `json:"manifestId"`
- Source string `json:"source"`
- RequestName string `json:"requestName"`
- GlobalData map[string]any `json:"globalData"`
- FieldValues map[string]string `json:"fieldValues"`
- Status string `json:"status"`
- Notes string `json:"notes"`
- }
-
- func (a *API) IntakeDraft(w http.ResponseWriter, r *http.Request) {
- var req upsertDraftRequest
- if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
- writeJSON(w, http.StatusBadRequest, map[string]any{"error": "invalid JSON body"})
- return
- }
- draft, err := a.draftSvc.SaveDraft(r.Context(), draftsvc.UpsertDraftRequest{
- TemplateID: req.TemplateID,
- ManifestID: req.ManifestID,
- Source: defaultStr(req.Source, "intake-api"),
- RequestName: req.RequestName,
- GlobalData: req.GlobalData,
- FieldValues: req.FieldValues,
- Status: defaultStr(req.Status, "draft"),
- Notes: req.Notes,
- })
- if err != nil {
- writeJSON(w, http.StatusBadRequest, map[string]any{"error": err.Error()})
- return
- }
- writeJSON(w, http.StatusCreated, draft)
- }
-
- func (a *API) ListDrafts(w http.ResponseWriter, r *http.Request) {
- limit, _ := strconv.Atoi(strings.TrimSpace(r.URL.Query().Get("limit")))
- drafts, err := a.draftSvc.ListDrafts(r.Context(), limit)
- if err != nil {
- writeJSON(w, http.StatusBadRequest, map[string]any{"error": err.Error()})
- return
- }
- writeJSON(w, http.StatusOK, map[string]any{"count": len(drafts), "drafts": drafts})
- }
-
- func (a *API) GetDraft(w http.ResponseWriter, r *http.Request) {
- draftID := strings.TrimSpace(chi.URLParam(r, "id"))
- draft, err := a.draftSvc.GetDraft(r.Context(), draftID)
- if err != nil {
- writeJSON(w, http.StatusNotFound, map[string]any{"error": err.Error()})
- return
- }
- writeJSON(w, http.StatusOK, draft)
- }
-
- func (a *API) UpdateDraft(w http.ResponseWriter, r *http.Request) {
- draftID := strings.TrimSpace(chi.URLParam(r, "id"))
- var req upsertDraftRequest
- if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
- writeJSON(w, http.StatusBadRequest, map[string]any{"error": "invalid JSON body"})
- return
- }
- draft, err := a.draftSvc.SaveDraft(r.Context(), draftsvc.UpsertDraftRequest{
- DraftID: draftID,
- TemplateID: req.TemplateID,
- ManifestID: req.ManifestID,
- Source: req.Source,
- RequestName: req.RequestName,
- GlobalData: req.GlobalData,
- FieldValues: req.FieldValues,
- Status: req.Status,
- Notes: req.Notes,
- })
- if err != nil {
- writeJSON(w, http.StatusBadRequest, map[string]any{"error": err.Error()})
- return
- }
- writeJSON(w, http.StatusOK, draft)
- }
-
- func (a *API) GetBuild(w http.ResponseWriter, r *http.Request) {
- buildID := chi.URLParam(r, "id")
- build, err := a.buildSvc.GetBuild(r.Context(), buildID)
- if err != nil {
- writeJSON(w, http.StatusNotFound, map[string]any{"error": err.Error()})
- return
- }
- writeJSON(w, http.StatusOK, build)
- }
-
- func (a *API) PollBuildOnce(w http.ResponseWriter, r *http.Request) {
- buildID := chi.URLParam(r, "id")
- if err := a.buildSvc.PollOnce(r.Context(), buildID); err != nil {
- writeJSON(w, http.StatusBadRequest, map[string]any{"error": err.Error()})
- return
- }
-
- build, err := a.buildSvc.GetBuild(r.Context(), buildID)
- if err != nil {
- writeJSON(w, http.StatusNotFound, map[string]any{"error": err.Error()})
- return
- }
- writeJSON(w, http.StatusOK, build)
- }
-
- func (a *API) FetchBuildEditorURL(w http.ResponseWriter, r *http.Request) {
- buildID := chi.URLParam(r, "id")
- if err := a.buildSvc.FetchEditorURL(r.Context(), buildID); err != nil {
- writeJSON(w, http.StatusBadRequest, map[string]any{"error": err.Error()})
- return
- }
-
- build, err := a.buildSvc.GetBuild(r.Context(), buildID)
- if err != nil {
- writeJSON(w, http.StatusNotFound, map[string]any{"error": err.Error()})
- return
- }
- writeJSON(w, http.StatusOK, build)
- }
-
- func writeJSON(w http.ResponseWriter, status int, v any) {
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(status)
- _ = json.NewEncoder(w).Encode(v)
- }
-
- func defaultStr(v, fallback string) string {
- if strings.TrimSpace(v) == "" {
- return fallback
- }
- return strings.TrimSpace(v)
- }
|