package serverless import ( "context" "io" "net/http" "strconv" "strings" "time" "github.com/DeBrosOfficial/network/pkg/serverless" ) // InvokeFunction handles POST /v1/functions/{name}/invoke // Invokes a function with the provided input. func (h *ServerlessHandlers) InvokeFunction(w http.ResponseWriter, r *http.Request, nameWithNS string, version int) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } // Parse namespace and name var namespace, name string if idx := strings.Index(nameWithNS, "/"); idx > 0 { namespace = nameWithNS[:idx] name = nameWithNS[idx+1:] } else { name = nameWithNS namespace = r.URL.Query().Get("namespace") if namespace == "" { namespace = h.getNamespaceFromRequest(r) } } if namespace == "" { writeError(w, http.StatusBadRequest, "namespace required") return } // Read input body input, err := io.ReadAll(io.LimitReader(r.Body, 1<<20)) // 1MB max if err != nil { writeError(w, http.StatusBadRequest, "Failed to read request body") return } // Get caller wallet from JWT callerWallet := h.getWalletFromRequest(r) ctx, cancel := context.WithTimeout(r.Context(), 60*time.Second) defer cancel() req := &serverless.InvokeRequest{ Namespace: namespace, FunctionName: name, Version: version, Input: input, TriggerType: serverless.TriggerTypeHTTP, CallerWallet: callerWallet, } resp, err := h.invoker.Invoke(ctx, req) if err != nil { statusCode := http.StatusInternalServerError if serverless.IsNotFound(err) { statusCode = http.StatusNotFound } else if serverless.IsResourceExhausted(err) { statusCode = http.StatusTooManyRequests } else if serverless.IsUnauthorized(err) { statusCode = http.StatusUnauthorized } writeJSON(w, statusCode, map[string]interface{}{ "request_id": resp.RequestID, "status": resp.Status, "error": resp.Error, "duration_ms": resp.DurationMS, }) return } // Return the function's output directly if it's JSON w.Header().Set("X-Request-ID", resp.RequestID) w.Header().Set("X-Duration-Ms", strconv.FormatInt(resp.DurationMS, 10)) // Try to detect if output is JSON if len(resp.Output) > 0 && (resp.Output[0] == '{' || resp.Output[0] == '[') { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write(resp.Output) } else { writeJSON(w, http.StatusOK, map[string]interface{}{ "request_id": resp.RequestID, "output": string(resp.Output), "status": resp.Status, "duration_ms": resp.DurationMS, }) } } // HandleInvoke handles POST /v1/invoke/{namespace}/{name}[@version] // Direct invocation endpoint with namespace in path. func (h *ServerlessHandlers) HandleInvoke(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } // Parse path: /v1/invoke/{namespace}/{name}[@version] path := strings.TrimPrefix(r.URL.Path, "/v1/invoke/") parts := strings.SplitN(path, "/", 2) if len(parts) < 2 { http.Error(w, "Path must be /v1/invoke/{namespace}/{name}", http.StatusBadRequest) return } namespace := parts[0] name := parts[1] // Parse version if present version := 0 if idx := strings.Index(name, "@"); idx > 0 { vStr := name[idx+1:] name = name[:idx] if v, err := strconv.Atoi(vStr); err == nil { version = v } } h.InvokeFunction(w, r, namespace+"/"+name, version) } // GetFunctionInfo handles GET /v1/functions/{name} // Returns detailed information about a specific function. func (h *ServerlessHandlers) GetFunctionInfo(w http.ResponseWriter, r *http.Request, name string, version int) { namespace := r.URL.Query().Get("namespace") if namespace == "" { namespace = h.getNamespaceFromRequest(r) } if namespace == "" { writeError(w, http.StatusBadRequest, "namespace required") return } ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second) defer cancel() fn, err := h.registry.Get(ctx, namespace, name, version) if err != nil { if serverless.IsNotFound(err) { writeError(w, http.StatusNotFound, "Function not found") } else { writeError(w, http.StatusInternalServerError, "Failed to get function") } return } writeJSON(w, http.StatusOK, fn) } // ListVersions handles GET /v1/functions/{name}/versions // Lists all versions of a specific function. func (h *ServerlessHandlers) ListVersions(w http.ResponseWriter, r *http.Request, name string) { namespace := r.URL.Query().Get("namespace") if namespace == "" { namespace = h.getNamespaceFromRequest(r) } if namespace == "" { writeError(w, http.StatusBadRequest, "namespace required") return } ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second) defer cancel() // Get registry with extended methods reg, ok := h.registry.(*serverless.Registry) if !ok { writeError(w, http.StatusNotImplemented, "Version listing not supported") return } versions, err := reg.ListVersions(ctx, namespace, name) if err != nil { writeError(w, http.StatusInternalServerError, "Failed to list versions") return } writeJSON(w, http.StatusOK, map[string]interface{}{ "versions": versions, "count": len(versions), }) }