package api import ( "encoding/json" "fmt" "io" "net/http" "time" "MobileMkch/cache" "MobileMkch/models" ) const ( BaseURL = "https://mkch.pooziqo.xyz" ApiURL = BaseURL + "/api" ) type Client struct { httpClient *http.Client baseURL string debug bool } func NewClient() *Client { return &Client{ httpClient: &http.Client{ Timeout: 30 * time.Second, }, baseURL: ApiURL, debug: false, } } func (c *Client) EnableDebug(enable bool) { c.debug = enable } func (c *Client) makeRequest(url string) ([]byte, error) { if c.debug { fmt.Printf("[API] Запрос: %s\n", url) } resp, err := c.httpClient.Get(url) if err != nil { return nil, fmt.Errorf("ошибка запроса: %w", err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("ошибка чтения ответа: %w", err) } if c.debug { fmt.Printf("[API] Статус: %d, Длина ответа: %d байт\n", resp.StatusCode, len(body)) if len(body) < 500 { fmt.Printf("[API] Ответ: %s\n", string(body)) } } if resp.StatusCode != http.StatusOK { return nil, &models.APIError{ Message: fmt.Sprintf("HTTP %d: %s", resp.StatusCode, string(body)), Code: resp.StatusCode, } } return body, nil } func (c *Client) GetBoards() ([]models.Board, error) { // Поздравляю, кеш if boards, exists := cache.GetCache().GetBoards(); exists { if c.debug { fmt.Printf("[API] Доски загружены из кэша\n") } return boards, nil } url := c.baseURL + "/boards/" body, err := c.makeRequest(url) if err != nil { return nil, fmt.Errorf("ошибка получения досок: %w", err) } var boards []models.Board if err := json.Unmarshal(body, &boards); err != nil { if c.debug { fmt.Printf("[API] Ошибка парсинга JSON: %v\n", err) fmt.Printf("[API] Ответ был: %s\n", string(body)) } return nil, fmt.Errorf("ошибка парсинга досок: %w", err) } // Всех победили cache.GetCache().SetBoards(boards) if c.debug { fmt.Printf("[API] Получено досок: %d\n", len(boards)) } return boards, nil } func (c *Client) GetThreads(boardCode string) ([]models.Thread, error) { if threads, exists := cache.GetCache().GetThreads(boardCode); exists { if c.debug { fmt.Printf("[API] Треды загружены из кэша для /%s/\n", boardCode) } return threads, nil } url := fmt.Sprintf("%s/board/%s", c.baseURL, boardCode) body, err := c.makeRequest(url) if err != nil { return nil, fmt.Errorf("ошибка получения тредов доски %s: %w", boardCode, err) } var threads []models.Thread if err := json.Unmarshal(body, &threads); err != nil { if c.debug { fmt.Printf("[API] Ошибка парсинга JSON тредов: %v\n", err) fmt.Printf("[API] Ответ был: %s\n", string(body)) } return nil, fmt.Errorf("ошибка парсинга тредов: %w", err) } // if len(threads) > 12 { // if c.debug { // fmt.Printf("[API] Ограничиваем количество тредов с %d до 5\n", len(threads)) // } // threads = threads[:12] // } cache.GetCache().SetThreads(boardCode, threads) if c.debug { fmt.Printf("[API] Получено тредов в /%s/: %d\n", boardCode, len(threads)) } return threads, nil } func (c *Client) GetThread(boardCode string, threadID int) (*models.ThreadDetail, error) { if thread, exists := cache.GetCache().GetThreadDetail(threadID); exists { if c.debug { fmt.Printf("[API] Тред %d загружен из кэша\n", threadID) } return thread, nil } url := fmt.Sprintf("%s/board/%s/thread/%d", c.baseURL, boardCode, threadID) body, err := c.makeRequest(url) if err != nil { return nil, fmt.Errorf("ошибка получения треда %d: %w", threadID, err) } var thread models.ThreadDetail if err := json.Unmarshal(body, &thread); err != nil { if c.debug { fmt.Printf("[API] Ошибка парсинга JSON треда: %v\n", err) fmt.Printf("[API] Ответ был: %s\n", string(body)) } return nil, fmt.Errorf("ошибка парсинга треда: %w", err) } cache.GetCache().SetThreadDetail(threadID, &thread) if c.debug { fmt.Printf("[API] Получен тред: ID=%d, Title=%s\n", thread.ID, thread.Title) } return &thread, nil } func (c *Client) GetComments(boardCode string, threadID int) ([]models.Comment, error) { if comments, exists := cache.GetCache().GetComments(threadID); exists { if c.debug { fmt.Printf("[API] Комментарии загружены из кэша для треда %d\n", threadID) } return comments, nil } url := fmt.Sprintf("%s/board/%s/thread/%d/comments", c.baseURL, boardCode, threadID) body, err := c.makeRequest(url) if err != nil { return nil, fmt.Errorf("ошибка получения комментариев треда %d: %w", threadID, err) } var comments []models.Comment if err := json.Unmarshal(body, &comments); err != nil { if c.debug { fmt.Printf("[API] Ошибка парсинга JSON комментариев: %v\n", err) fmt.Printf("[API] Ответ был: %s\n", string(body)) } return nil, fmt.Errorf("ошибка парсинга комментариев: %w", err) } cache.GetCache().SetComments(threadID, comments) if c.debug { fmt.Printf("[API] Получено комментариев: %d\n", len(comments)) } return comments, nil } func (c *Client) GetFullThread(boardCode string, threadID int) (*models.ThreadDetail, []models.Comment, error) { threadChan := make(chan *models.ThreadDetail, 1) commentsChan := make(chan []models.Comment, 1) errorChan := make(chan error, 2) go func() { thread, err := c.GetThread(boardCode, threadID) if err != nil { errorChan <- fmt.Errorf("ошибка получения треда: %w", err) return } threadChan <- thread }() go func() { comments, err := c.GetComments(boardCode, threadID) if err != nil { errorChan <- fmt.Errorf("ошибка получения комментариев: %w", err) return } commentsChan <- comments }() var thread *models.ThreadDetail var comments []models.Comment var errors []error for i := 0; i < 2; i++ { select { case t := <-threadChan: thread = t case c := <-commentsChan: comments = c case err := <-errorChan: errors = append(errors, err) case <-time.After(30 * time.Second): return nil, nil, fmt.Errorf("тайм-аут получения треда") } } if len(errors) > 0 { return nil, nil, errors[0] } return thread, comments, nil }