package services import ( "bytes" "encoding/json" "fmt" "io" "net/http" "time" ) // ShieldClient cliente HTTP para la API de USITE Shield. type ShieldClient struct { baseURL string adminToken string http *http.Client } func NewShieldClient(baseURL, adminToken string) *ShieldClient { if baseURL == "" { baseURL = "https://api-shield.u-s.app" } return &ShieldClient{ baseURL: baseURL, adminToken: adminToken, http: &http.Client{Timeout: 15 * time.Second}, } } func (s *ShieldClient) do(method, path string, body interface{}) ([]byte, int, error) { var bodyReader io.Reader if body != nil { b, err := json.Marshal(body) if err != nil { return nil, 0, err } bodyReader = bytes.NewReader(b) } req, err := http.NewRequest(method, s.baseURL+path, bodyReader) if err != nil { return nil, 0, err } req.Header.Set("Content-Type", "application/json") if s.adminToken != "" { req.Header.Set("Authorization", "Bearer "+s.adminToken) } resp, err := s.http.Do(req) if err != nil { return nil, 0, err } defer resp.Body.Close() data, err := io.ReadAll(resp.Body) if err != nil { return nil, resp.StatusCode, err } if resp.StatusCode >= 400 { // La API puede devolver texto plano en errores; evitamos que el caller intente parsear JSON msg := string(data) if msg == "" { msg = resp.Status } return nil, resp.StatusCode, fmt.Errorf("shield API error %d: %s", resp.StatusCode, msg) } return data, resp.StatusCode, nil } // ─── Health ────────────────────────────────────────────────────────────────── func (s *ShieldClient) Health() (map[string]interface{}, error) { data, _, err := s.do("GET", "/health", nil) if err != nil { return nil, err } var out map[string]interface{} return out, json.Unmarshal(data, &out) } // ─── Extension version ─────────────────────────────────────────────────────── func (s *ShieldClient) ExtensionVersion() (map[string]interface{}, error) { data, _, err := s.do("GET", "/api/extension/version", nil) if err != nil { return nil, err } var out map[string]interface{} return out, json.Unmarshal(data, &out) } // ─── Logs ──────────────────────────────────────────────────────────────────── func (s *ShieldClient) Logs(limit int, path, method string) (map[string]interface{}, error) { q := fmt.Sprintf("/api/admin/logs?limit=%d", limit) if path != "" { q += "&path=" + path } if method != "" { q += "&method=" + method } data, _, err := s.do("GET", q, nil) if err != nil { return nil, err } var out map[string]interface{} return out, json.Unmarshal(data, &out) } func (s *ShieldClient) LogStats() (map[string]interface{}, error) { data, _, err := s.do("GET", "/api/admin/logs/stats", nil) if err != nil { return nil, err } var out map[string]interface{} return out, json.Unmarshal(data, &out) } // ─── Review requests ───────────────────────────────────────────────────────── func (s *ShieldClient) ReviewRequests(status string) (map[string]interface{}, error) { if status == "" { status = "pending" } data, _, err := s.do("GET", "/api/admin/review-requests?status="+status, nil) if err != nil { return nil, err } var out map[string]interface{} return out, json.Unmarshal(data, &out) } func (s *ShieldClient) ApproveRequest(id, note, reason string) (map[string]interface{}, error) { body := map[string]string{"note": note, "reason": reason} data, _, err := s.do("PUT", "/api/admin/review-requests/"+id+"/approve", body) if err != nil { return nil, err } var out map[string]interface{} json.Unmarshal(data, &out) return out, nil } func (s *ShieldClient) RejectRequest(id, note string) (map[string]interface{}, error) { body := map[string]string{"note": note} data, _, err := s.do("PUT", "/api/admin/review-requests/"+id+"/reject", body) if err != nil { return nil, err } var out map[string]interface{} json.Unmarshal(data, &out) return out, nil } // ─── Whitelist ─────────────────────────────────────────────────────────────── func (s *ShieldClient) Whitelist() (map[string]interface{}, error) { data, _, err := s.do("GET", "/api/admin/whitelist", nil) if err != nil { return nil, err } var out map[string]interface{} return out, json.Unmarshal(data, &out) } func (s *ShieldClient) AddWhitelist(domain, reason, addedBy string) (map[string]interface{}, error) { body := map[string]string{"domain": domain, "reason": reason, "added_by": addedBy} data, _, err := s.do("POST", "/api/admin/whitelist", body) if err != nil { return nil, err } var out map[string]interface{} json.Unmarshal(data, &out) return out, nil } func (s *ShieldClient) DeleteWhitelist(domain string) error { _, _, err := s.do("DELETE", "/api/admin/whitelist/"+domain, nil) return err } // ─── Blacklist ─────────────────────────────────────────────────────────────── func (s *ShieldClient) Blacklist() (map[string]interface{}, error) { data, _, err := s.do("GET", "/api/admin/blacklist", nil) if err != nil { return nil, err } var out map[string]interface{} return out, json.Unmarshal(data, &out) } func (s *ShieldClient) AddBlacklist(domain, reason, addedBy string) (map[string]interface{}, error) { body := map[string]string{"domain": domain, "reason": reason, "added_by": addedBy} data, _, err := s.do("POST", "/api/admin/blacklist", body) if err != nil { return nil, err } var out map[string]interface{} json.Unmarshal(data, &out) return out, nil } func (s *ShieldClient) DeleteBlacklist(domain string) error { _, _, err := s.do("DELETE", "/api/admin/blacklist/"+domain, nil) return err } // ─── Reputación ────────────────────────────────────────────────────────────── func (s *ShieldClient) Reputation(domain string) (map[string]interface{}, error) { data, _, err := s.do("GET", "/api/reputation/"+domain, nil) if err != nil { return nil, err } var out map[string]interface{} return out, json.Unmarshal(data, &out) } func (s *ShieldClient) AdjustScore(domain string, votesReal, votesFake int) (map[string]interface{}, error) { body := map[string]int{"votes_real": votesReal, "votes_fake": votesFake} data, _, err := s.do("PUT", "/api/admin/reputation/"+domain+"/score", body) if err != nil { return nil, err } var out map[string]interface{} json.Unmarshal(data, &out) return out, nil }