MobileMkch/api/client.go
2025-08-05 20:08:15 +03:00

255 lines
6.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}