| package orchestrator |
|
|
| import ( |
| "bytes" |
| "encoding/json" |
| "fmt" |
| "io" |
| "net/http" |
| "time" |
|
|
| "github.com/pinchtab/pinchtab/internal/web" |
| ) |
|
|
| |
| |
| func (o *Orchestrator) handleTabClose(w http.ResponseWriter, r *http.Request) { |
| tabID := r.PathValue("id") |
| if tabID == "" { |
| web.Error(w, 400, fmt.Errorf("tab id required")) |
| return |
| } |
|
|
| inst, err := o.findRunningInstanceByTabID(tabID) |
| if err != nil { |
| web.Error(w, 404, err) |
| return |
| } |
|
|
| |
| reqBody, _ := json.Marshal(map[string]string{ |
| "action": "close", |
| "tabId": tabID, |
| }) |
|
|
| targetURL, err := o.instancePathURL(inst, "/tab", "") |
| if err != nil { |
| web.Error(w, 502, err) |
| return |
| } |
| proxyReq, err := http.NewRequestWithContext(r.Context(), "POST", targetURL.String(), bytes.NewReader(reqBody)) |
| if err != nil { |
| web.Error(w, 500, err) |
| return |
| } |
| proxyReq.Header.Set("Content-Type", "application/json") |
| o.applyInstanceAuth(proxyReq, inst) |
|
|
| client := &http.Client{Timeout: 30 * time.Second} |
| resp, err := client.Do(proxyReq) |
| if err != nil { |
| web.Error(w, 502, fmt.Errorf("instance unreachable: %w", err)) |
| return |
| } |
| defer func() { _ = resp.Body.Close() }() |
|
|
| body, _ := io.ReadAll(resp.Body) |
| w.Header().Set("Content-Type", "application/json") |
| w.WriteHeader(resp.StatusCode) |
| _, _ = w.Write(body) |
| } |
|
|
| |
| |
| func (o *Orchestrator) handleInstanceTabOpen(w http.ResponseWriter, r *http.Request) { |
| id := r.PathValue("id") |
|
|
| o.mu.RLock() |
| inst, ok := o.instances[id] |
| o.mu.RUnlock() |
| if !ok { |
| web.Error(w, 404, fmt.Errorf("instance %q not found", id)) |
| return |
| } |
| if inst.Status != "running" { |
| web.Error(w, 503, fmt.Errorf("instance %q is not running (status: %s)", id, inst.Status)) |
| return |
| } |
|
|
| var req struct { |
| URL string `json:"url,omitempty"` |
| } |
| if r.ContentLength > 0 { |
| if err := json.NewDecoder(r.Body).Decode(&req); err != nil { |
| web.Error(w, 400, fmt.Errorf("invalid JSON")) |
| return |
| } |
| } |
|
|
| payload, err := json.Marshal(map[string]any{ |
| "action": "new", |
| "url": req.URL, |
| }) |
| if err != nil { |
| web.Error(w, 500, fmt.Errorf("failed to build tab open request: %w", err)) |
| return |
| } |
|
|
| proxyReq := r.Clone(r.Context()) |
| proxyReq.Body = io.NopCloser(bytes.NewReader(payload)) |
| proxyReq.ContentLength = int64(len(payload)) |
| proxyReq.Header = r.Header.Clone() |
| proxyReq.Header.Set("Content-Type", "application/json") |
|
|
| targetURL, err := o.instancePathURL(inst, "/tab", r.URL.RawQuery) |
| if err != nil { |
| web.Error(w, 502, err) |
| return |
| } |
| o.proxyToURL(w, proxyReq, targetURL) |
| } |
|
|