package httpapi import ( "encoding/json" "fmt" "net/http" "time" "github.com/easyai/easyai-ai-gateway/apps/api/internal/auth" "github.com/easyai/easyai-ai-gateway/apps/api/internal/store" ) func (s *Server) health(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusOK, map[string]any{ "ok": true, "service": "easyai-ai-gateway", "env": s.cfg.AppEnv, }) } func (s *Server) ready(w http.ResponseWriter, r *http.Request) { if err := s.store.Ping(r.Context()); err != nil { writeError(w, http.StatusServiceUnavailable, "postgres unavailable") return } writeJSON(w, http.StatusOK, map[string]any{"ok": true}) } func (s *Server) me(w http.ResponseWriter, r *http.Request) { user, _ := auth.UserFromContext(r.Context()) writeJSON(w, http.StatusOK, user) } func (s *Server) listPlatforms(w http.ResponseWriter, r *http.Request) { platforms, err := s.store.ListPlatforms(r.Context()) if err != nil { s.logger.Error("list platforms failed", "error", err) writeError(w, http.StatusInternalServerError, "list platforms failed") return } writeJSON(w, http.StatusOK, map[string]any{"items": platforms}) } func (s *Server) createPlatform(w http.ResponseWriter, r *http.Request) { var input store.CreatePlatformInput if err := json.NewDecoder(r.Body).Decode(&input); err != nil { writeError(w, http.StatusBadRequest, "invalid json body") return } if input.Provider == "" || input.Name == "" { writeError(w, http.StatusBadRequest, "provider and name are required") return } if input.AuthType == "" { input.AuthType = "bearer" } platform, err := s.store.CreatePlatform(r.Context(), input) if err != nil { s.logger.Error("create platform failed", "error", err) writeError(w, http.StatusInternalServerError, "create platform failed") return } writeJSON(w, http.StatusCreated, platform) } func (s *Server) listModels(w http.ResponseWriter, r *http.Request) { models, err := s.store.ListModels(r.Context()) if err != nil { s.logger.Error("list models failed", "error", err) writeError(w, http.StatusInternalServerError, "list models failed") return } writeJSON(w, http.StatusOK, map[string]any{"items": models}) } func (s *Server) listCatalogProviders(w http.ResponseWriter, r *http.Request) { items, err := s.store.ListCatalogProviders(r.Context()) if err != nil { s.logger.Error("list catalog providers failed", "error", err) writeError(w, http.StatusInternalServerError, "list catalog providers failed") return } writeJSON(w, http.StatusOK, map[string]any{"items": items}) } func (s *Server) listBaseModels(w http.ResponseWriter, r *http.Request) { items, err := s.store.ListBaseModels(r.Context()) if err != nil { s.logger.Error("list base models failed", "error", err) writeError(w, http.StatusInternalServerError, "list base models failed") return } writeJSON(w, http.StatusOK, map[string]any{"items": items}) } func (s *Server) listPricingRules(w http.ResponseWriter, r *http.Request) { items, err := s.store.ListPricingRules(r.Context()) if err != nil { s.logger.Error("list pricing rules failed", "error", err) writeError(w, http.StatusInternalServerError, "list pricing rules failed") return } writeJSON(w, http.StatusOK, map[string]any{"items": items}) } func (s *Server) estimatePricing(w http.ResponseWriter, r *http.Request) { var body map[string]any if err := json.NewDecoder(r.Body).Decode(&body); err != nil { writeError(w, http.StatusBadRequest, "invalid json body") return } writeJSON(w, http.StatusOK, map[string]any{ "items": []any{}, "resolver": "effective-pricing-placeholder", "request": body, }) } func (s *Server) listRateLimitWindows(w http.ResponseWriter, r *http.Request) { items, err := s.store.ListRateLimitWindows(r.Context()) if err != nil { s.logger.Error("list rate limit windows failed", "error", err) writeError(w, http.StatusInternalServerError, "list rate limit windows failed") return } writeJSON(w, http.StatusOK, map[string]any{"items": items}) } func (s *Server) createTask(kind string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { user, ok := auth.UserFromContext(r.Context()) if !ok { writeError(w, http.StatusUnauthorized, "unauthorized") return } var body map[string]any if err := json.NewDecoder(r.Body).Decode(&body); err != nil { writeError(w, http.StatusBadRequest, "invalid json body") return } model, _ := body["model"].(string) if model == "" { writeError(w, http.StatusBadRequest, "model is required") return } task, err := s.store.CreateTask(r.Context(), store.CreateTaskInput{ Kind: kind, Model: model, Request: body, }, user) if err != nil { s.logger.Error("create task failed", "kind", kind, "error", err) writeError(w, http.StatusInternalServerError, "create task failed") return } writeJSON(w, http.StatusAccepted, map[string]any{ "task": task, "next": map[string]string{ "events": fmt.Sprintf("/api/v1/tasks/%s/events", task.ID), "detail": fmt.Sprintf("/api/v1/tasks/%s", task.ID), }, }) }) } func (s *Server) getTask(w http.ResponseWriter, r *http.Request) { task, err := s.store.GetTask(r.Context(), r.PathValue("taskID")) if err == nil { writeJSON(w, http.StatusOK, task) return } if store.IsNotFound(err) { writeError(w, http.StatusNotFound, "task not found") return } s.logger.Error("get task failed", "error", err) writeError(w, http.StatusInternalServerError, "get task failed") } func (s *Server) taskEvents(w http.ResponseWriter, r *http.Request) { task, err := s.store.GetTask(r.Context(), r.PathValue("taskID")) if err != nil { if store.IsNotFound(err) { writeError(w, http.StatusNotFound, "task not found") return } writeError(w, http.StatusInternalServerError, "get task failed") return } w.Header().Set("Content-Type", "text/event-stream") w.Header().Set("Cache-Control", "no-cache") w.Header().Set("Connection", "keep-alive") sendSSE(w, "task.accepted", map[string]any{ "taskId": task.ID, "status": task.Status, }) if flusher, ok := w.(http.Flusher); ok { flusher.Flush() } timer := time.NewTimer(250 * time.Millisecond) defer timer.Stop() select { case <-r.Context().Done(): return case <-timer.C: sendSSE(w, "task.placeholder", map[string]any{ "taskId": task.ID, "message": "runtime worker is not wired yet", }) } }