# Руководство по API для фронтенда ## Обзор изменений После рефакторинга старый `ParserController` был разделен на три специализированных контроллера для лучшей организации и масштабируемости: - **`MarketItemController`** (`/api/items`) - для получения данных - **`ParserAdminController`** (`/api/admin/parsers`) - для управления парсерами - **`HealthCheckController`** (`/api/health`) - для мониторинга системы --- ## 📊 MarketItemController - Получение данных **Базовый URL**: `/api/items` ### 1. Получение списка новостей с фильтрацией и пагинацией ```http GET /api/items ``` **Параметры запроса:** - `sourceName` (optional) - фильтр по источнику (например: "Kursiv Media", "Kapital.kz", "LSM.kz", "РБК", "Ведомости") - `startDate` (optional) - начальная дата в формате ISO (например: "2024-01-01T00:00:00") - `endDate` (optional) - конечная дата в формате ISO (например: "2024-12-31T23:59:59") - `page` (optional) - номер страницы (по умолчанию: 0) - `size` (optional) - размер страницы (по умолчанию: 20) - `sort` (optional) - сортировка (по умолчанию: "publishedAt,desc") **Примеры запросов:** ```javascript // Получить все новости fetch('/api/items'); // Получить новости с пагинацией fetch('/api/items?page=0&size=10'); // Получить новости от конкретного источника fetch('/api/items?sourceName=Kursiv Media'); // Получить новости за последние 7 дней const weekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(); fetch(`/api/items?startDate=${weekAgo}`); // Получить новости с сортировкой по дате (старые сначала) fetch('/api/items?sort=publishedAt,asc'); ``` **Ответ:** ```json { "success": true, "message": "Записи получены успешно", "data": { "content": [ { "id": "64f1a2b3c4d5e6f7g8h9i0j1", "sourceName": "Kursiv Media", "url": "https://kursiv.media/news/example", "title": "Заголовок новости", "publishedAt": "2024-01-15T10:30:00", "addedAt": "2024-01-15T10:35:00", "rawText": "Текст новости без HTML тегов", "hash": "abc123def456...", "category": "Бизнес", "analytics": { "summary": null, "sentiment": null, "tags": [], "entities": {} } } ], "pageable": { "sort": { "sorted": true, "unsorted": false }, "pageNumber": 0, "pageSize": 20, "offset": 0, "paged": true, "unpaged": false }, "totalElements": 150, "totalPages": 8, "last": false, "first": true, "numberOfElements": 20, "size": 20, "number": 0, "empty": false } } ``` ### 2. Получение новости по ID ```http GET /api/items/{id} ``` **Пример:** ```javascript fetch('/api/items/64f1a2b3c4d5e6f7g8h9i0j1'); ``` **Ответ:** ```json { "success": false, "message": "Функция поиска по ID пока не реализована" } ``` ### 3. Получение статистики ```http GET /api/items/stats ``` **Ответ:** ```json { "success": true, "message": "Статистика получена успешно", "data": { "totalItems": 1250, "sourceStatistics": { "Kursiv Media": 300, "Kapital.kz": 250, "LSM.kz": 200, "РБК": 300, "Ведомости": 200 }, "lastUpdate": "2024-01-15T10:35:00" } } ``` ### 4. Получение списка источников (sourceName) ```http GET /api/items/sources ``` **Описание:** Возвращает список всех уникальных источников новостей, которые есть в базе данных. Этот эндпоинт полезен для: - Построения фильтров по источникам - Отображения списка доступных источников в UI - Динамического создания выпадающих списков **Ответ:** ```json { "success": true, "message": "Источники получены успешно", "data": [ "Kursiv (Бизнес/экономика)", "Kapital.kz (Бизнес)", "LSM.kz", "РБК", "Ведомости" ] } ``` **Примеры использования:** ```javascript // Получить список всех источников const response = await fetch('/api/items/sources'); const result = await response.json(); if (result.success) { const sources = result.data; console.log('Доступные источники:', sources); // sources = ["Kursiv (Бизнес/экономика)", "Kapital.kz (Бизнес)", "LSM.kz", "РБК", "Ведомости"] } // Использование в React для создания фильтра function SourceFilter({ onSourceChange }) { const [sources, setSources] = useState([]); useEffect(() => { fetch('/api/items/sources') .then((res) => res.json()) .then((data) => { if (data.success) { setSources(data.data); } }); }, []); return ( ); } // Использование в Vue.js export default { data() { return { sources: [], selectedSource: '', }; }, async mounted() { const response = await fetch('/api/items/sources'); const result = await response.json(); if (result.success) { this.sources = result.data; } }, }; ``` **Важные замечания:** - Список источников формируется на основе реальных данных в базе - Если в базе нет данных от какого-то источника, он не будет включен в список - Названия источников точно соответствуют тем, что используются в поле `sourceName` при фильтрации - Порядок источников может изменяться в зависимости от того, как MongoDB возвращает уникальные значения ### 5. Получение новостей по конкретному источнику ```http GET /api/items/source/{sourceName} ``` **Пример:** ```javascript fetch('/api/items/source/Kursiv Media?page=0&size=10'); ``` **Ответ:** Аналогичен ответу от `/api/items`, но только с новостями от указанного источника. --- ## ⚙️ ParserAdminController - Управление парсерами **Базовый URL**: `/api/admin/parsers` > ⚠️ **Важно**: Эти эндпоинты предназначены для административных функций и могут потребовать аутентификации в будущем. ### 1. Запуск конкретного парсера ```http POST /api/admin/parsers/parse/{sourceName} ``` **Доступные источники:** - `kursiv` - `kapital` - `lsm` - `rbc` - `vedomosti` **Пример:** ```javascript // Запустить парсер Kursiv fetch('/api/admin/parsers/parse/kursiv', { method: 'POST' }); // Запустить парсер РБК fetch('/api/admin/parsers/parse/rbc', { method: 'POST' }); ``` **Ответ:** ```json { "success": true, "message": "Парсинг kursiv завершен", "data": { "source": "kursiv", "processedItems": 15, "totalItemsInDb": 1265, "status": "completed" } } ``` ### 2. Запуск всех парсеров ```http POST /api/admin/parsers/parse/all ``` **Пример:** ```javascript fetch('/api/admin/parsers/parse/all', { method: 'POST' }); ``` **Ответ:** ```json { "success": true, "message": "Парсинг всех источников завершен", "data": [ { "source": "kursiv", "processedItems": 12, "totalItemsInDb": 1277, "status": "completed" }, { "source": "kapital", "processedItems": 8, "totalItemsInDb": 1285, "status": "completed" }, { "source": "lsm", "processedItems": 5, "totalItemsInDb": 1290, "status": "completed" }, { "source": "rbc", "processedItems": 20, "totalItemsInDb": 1310, "status": "completed" }, { "source": "vedomosti", "processedItems": 7, "totalItemsInDb": 1317, "status": "completed" } ] } ``` ### 3. Получение списка доступных парсеров ```http GET /api/admin/parsers ``` **Ответ:** ```json { "success": true, "message": "Список парсеров получен", "data": ["kapital", "kursiv", "lsm", "rbc", "vedomosti"] } ``` ### 4. Получение информации о парсерах ```http GET /api/admin/parsers/info ``` **Ответ:** ```json { "success": true, "message": "Информация о парсерах получена", "data": { "totalParsers": 5, "availableParsers": ["kapital", "kursiv", "lsm", "rbc", "vedomosti"], "totalItems": 1317, "sourceStatistics": { "Kursiv Media": 300, "Kapital.kz": 250, "LSM.kz": 200, "РБК": 300, "Ведомости": 200 } } } ``` ### 5. Проверка существования парсера ```http GET /api/admin/parsers/exists/{sourceName} ``` **Пример:** ```javascript fetch('/api/admin/parsers/exists/kursiv'); ``` **Ответ:** ```json { "success": true, "message": "Проверка завершена", "data": true } ``` --- ## 🔍 HealthCheckController - Мониторинг системы **Базовый URL**: `/api/health` ### 1. Базовая проверка состояния ```http GET /api/health ``` **Ответ:** ```json { "success": true, "message": "Сервис работает", "data": { "status": "UP", "service": "RSS Parser System", "timestamp": 1705312500000, "details": "Enabled - runs every 30 minutes" } } ``` ### 2. Проверка подключения к MongoDB ```http GET /api/health/mongodb ``` **Ответ (успех):** ```json { "success": true, "message": "MongoDB подключение успешно", "data": { "status": "UP", "service": "MongoDB подключение успешно", "timestamp": 1705312500000, "details": "Всего записей: 1317" } } ``` **Ответ (ошибка):** ```json { "success": false, "message": "Ошибка подключения к MongoDB", "data": { "status": "DOWN", "service": "Ошибка подключения к MongoDB: Connection refused", "timestamp": 1705312500000, "suggestion": "Проверьте учетные данные в application.properties" } } ``` ### 3. Информация о планировщике ```http GET /api/health/scheduler ``` **Ответ:** ```json { "success": true, "message": "Информация о планировщике получена", "data": { "schedulerEnabled": true, "cronExpression": "0 0/30 * * * ?", "description": "Запуск каждые 30 минут (в 0 и 30 минут каждого часа)", "nextRun": "Следующий запуск будет в ближайшие 0 или 30 минут часа", "activeParsers": ["kapital", "kursiv", "lsm", "rbc", "vedomosti"], "totalParsers": 5, "sources": ["Kursiv Media", "Kapital.kz", "LSM.kz", "РБК", "Ведомости"] } } ``` ### 4. Общая информация о системе ```http GET /api/health/system ``` **Ответ:** ```json { "success": true, "message": "Информация о системе получена", "data": { "parsers": { "total": 5, "available": ["kapital", "kursiv", "lsm", "rbc", "vedomosti"] }, "data": { "totalItems": 1317, "sourceStatistics": { "Kursiv Media": 300, "Kapital.kz": 250, "LSM.kz": 200, "РБК": 300, "Ведомости": 200 } }, "system": { "javaVersion": "17.0.2", "osName": "Linux", "osVersion": "5.4.0-74-generic", "uptime": 1705312500000 } } } ``` ### 5. Проверка состояния парсеров ```http GET /api/health/parsers ``` **Ответ:** ```json { "success": true, "message": "Проверка парсеров завершена", "data": { "totalParsers": 5, "availableParsers": ["kapital", "kursiv", "lsm", "rbc", "vedomosti"], "status": "UP", "message": "Все парсеры доступны" } } ``` ### 6. Проверка доступности RSS-лент ```http GET /api/health/rss ``` **Описание:** Проверяет доступность всех RSS-лент и предоставляет рекомендации по альтернативным URL в случае недоступности. **Ответ:** ```json { "success": true, "message": "Проверка RSS-лент завершена", "data": { "overallStatus": "DEGRADED", "accessibilityResults": { "kursiv": true, "kapital": false, "lsm": true, "rbc": false, "vedomosti": true }, "totalFeeds": 5, "accessibleFeeds": 3, "unaccessibleFeeds": 2, "recommendations": { "kapital": "https://kapital.kz/feed/", "rbc": "https://rbc.ru/rss/" } } } ``` **Примеры использования:** ```javascript // Проверка доступности RSS-лент async function checkRssFeeds() { try { const response = await fetch('/api/health/rss'); const result = await response.json(); if (result.success) { const data = result.data; console.log('Общий статус:', data.overallStatus); console.log('Доступные ленты:', data.accessibleFeeds); console.log('Недоступные ленты:', data.unaccessibleFeeds); // Показать рекомендации для недоступных лент if ( data.recommendations && Object.keys(data.recommendations).length > 0 ) { console.log('Рекомендации по альтернативным URL:'); Object.entries(data.recommendations).forEach(([source, url]) => { console.log(`${source}: ${url}`); }); } } } catch (error) { console.error('Ошибка проверки RSS-лент:', error); } } // React компонент для отображения статуса RSS-лент function RssStatusWidget() { const [rssStatus, setRssStatus] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { fetch('/api/health/rss') .then((res) => res.json()) .then((data) => { if (data.success) { setRssStatus(data.data); } setLoading(false); }) .catch((err) => { console.error('Ошибка загрузки статуса RSS:', err); setLoading(false); }); }, []); if (loading) return