diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
new file mode 100644
index 0000000..3c5e64a
--- /dev/null
+++ b/ARCHITECTURE.md
@@ -0,0 +1,197 @@
+# Архитектура новостного интерфейса
+
+## Схема компонентов
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ Vue.js Frontend │
+├─────────────────────────────────────────────────────────────────┤
+│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
+│ │ NewsList │ │ NewsDashboard │ │ NewsAdmin │ │
+│ │ (Лента) │ │ (Статистика) │ │ (Управление) │ │
+│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
+│ │ │ │ │
+│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
+│ │ NewsFilters │ │ NewsDetail │ │ NewsService │ │
+│ │ (Фильтры) │ │ (Детали) │ │ (API слой) │ │
+│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
+└─────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────────┐
+│ News Parser API │
+├─────────────────────────────────────────────────────────────────┤
+│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
+│ │ GET /items │ │ GET /stats │ │ GET /health │ │
+│ │ (Новости) │ │ (Статистика) │ │ (Статус) │ │
+│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
+│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
+│ │ POST /parse/all │ │POST /parse/{id} │ │GET /health/mongo│ │
+│ │ (Парсинг всех) │ │ (Парсинг по ID) │ │ (Статус БД) │ │
+│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
+└─────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────────┐
+│ MongoDB Database │
+├─────────────────────────────────────────────────────────────────┤
+│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
+│ │ News Items │ │ Statistics │ │ Health Logs │ │
+│ │ (Новости) │ │ (Статистика) │ │ (Логи) │ │
+│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+## Поток данных
+
+### 1. Загрузка новостей
+
+```
+User → NewsList → NewsService → API → MongoDB
+ ← NewsFilters ←
+```
+
+### 2. Фильтрация
+
+```
+User → NewsFilters → NewsList → NewsService → API (с параметрами)
+```
+
+### 3. Статистика
+
+```
+User → NewsDashboard → NewsService → API → MongoDB
+```
+
+### 4. Управление парсером
+
+```
+User → NewsAdmin → NewsService → API → Parser Service → MongoDB
+```
+
+## Структура данных
+
+### NewsItem
+
+```javascript
+{
+ id: string,
+ sourceName: string,
+ title: string,
+ url: string,
+ publishedAt: string,
+ addedAt: string,
+ rawText: string,
+ hash: string,
+ category: string,
+ analytics: {
+ summary: string,
+ sentiment: string,
+ tags: string[],
+ entities: object
+ }
+}
+```
+
+### API Response
+
+```javascript
+{
+ success: boolean,
+ message: string,
+ data: {
+ content: NewsItem[],
+ pageable: object,
+ totalElements: number,
+ totalPages: number,
+ last: boolean,
+ first: boolean,
+ numberOfElements: number
+ }
+}
+```
+
+## Технологический стек
+
+### Frontend
+
+- **Vue 3** - основной фреймворк
+- **PrimeVue** - UI компоненты
+- **Tailwind CSS** - стилизация
+- **Chart.js** - графики и диаграммы
+- **Vue Router** - маршрутизация
+
+### Backend API
+
+- **REST API** - HTTP эндпоинты
+- **JSON** - формат данных
+- **MongoDB** - база данных
+
+### Инструменты разработки
+
+- **Vite** - сборщик
+- **ESLint** - линтер
+- **Prettier** - форматирование
+
+## Принципы архитектуры
+
+### 1. Разделение ответственности
+
+- **Компоненты** - только UI логика
+- **Сервисы** - работа с API
+- **Конфигурация** - настройки
+
+### 2. Переиспользование
+
+- **NewsService** - единая точка доступа к API
+- **NewsFilters** - переиспользуемый компонент
+- **Конфигурация** - централизованные настройки
+
+### 3. Производительность
+
+- **Ленивая загрузка** - компоненты загружаются по требованию
+- **Кэширование** - данные статистики кэшируются
+- **Оптимизация** - минимальные API запросы
+
+### 4. Масштабируемость
+
+- **Модульная структура** - легко добавлять новые функции
+- **Конфигурируемость** - настройки через переменные окружения
+- **Расширяемость** - простое добавление новых источников
+
+## Безопасность
+
+### 1. Валидация данных
+
+- **Проверка ответов API** - всегда проверяется поле `success`
+- **Обработка ошибок** - graceful degradation
+- **Санитизация** - очистка пользовательского ввода
+
+### 2. Аутентификация
+
+- **Требуется авторизация** - все маршруты защищены
+- **Роли пользователей** - админ функции только для админов
+
+### 3. CORS
+
+- **Настройка CORS** - на уровне API сервера
+- **Безопасные запросы** - только к настроенному API
+
+## Мониторинг и отладка
+
+### 1. Логирование
+
+- **Консольные логи** - для разработки
+- **Обработка ошибок** - централизованная система
+
+### 2. Метрики
+
+- **Статистика загрузки** - количество запросов
+- **Время отклика** - производительность API
+- **Статус системы** - мониторинг здоровья
+
+### 3. Отладка
+
+- **DevTools** - инструменты разработчика
+- **Network tab** - мониторинг запросов
+- **Console** - логи и ошибки
diff --git a/DEBUG_NEWS.md b/DEBUG_NEWS.md
new file mode 100644
index 0000000..d7805fa
--- /dev/null
+++ b/DEBUG_NEWS.md
@@ -0,0 +1,75 @@
+# Отладка проблем с новостным интерфейсом
+
+## Проблема: Не открываются страницы новостей
+
+### Шаги для диагностики:
+
+1. **Откройте консоль браузера** (F12 → Console)
+2. **Попробуйте кликнуть на "Лента новостей"** в меню
+3. **Проверьте ошибки в консоли**
+
+### Возможные причины и решения:
+
+#### 1. Ошибки импорта компонентов
+
+**Симптомы:** Ошибки типа "Failed to resolve component" или "Cannot resolve module"
+**Решение:** Убедитесь, что все PrimeVue компоненты импортированы
+
+#### 2. Ошибки в API запросах
+
+**Симптомы:** Ошибки сети или CORS
+**Решение:**
+
+- Проверьте, что бэкенд API запущен на https://api.konturai.kz
+- Создайте файл `.env` с содержимым: `VUE_APP_API_URL=https://api.konturai.kz`
+
+#### 3. Ошибки в коде компонентов
+
+**Симптомы:** JavaScript ошибки в консоли
+**Решение:** Проверьте синтаксис и логику компонентов
+
+### Быстрая проверка:
+
+1. **Проверьте, что сервер запущен:**
+
+ ```bash
+ npm run dev
+ ```
+
+2. **Проверьте консоль браузера** на наличие ошибок
+
+3. **Попробуйте перейти напрямую по URL:**
+ - http://localhost:3000/news
+ - http://localhost:3000/news/dashboard
+ - http://localhost:3000/admin/news
+
+### Если ничего не помогает:
+
+1. **Очистите кэш браузера** (Ctrl+Shift+R)
+2. **Перезапустите сервер разработки**
+3. **Проверьте, что все файлы сохранены**
+
+### Тестовый компонент:
+
+Если основные компоненты не работают, можно временно заменить их на простой тестовый компонент:
+
+```vue
+
+
+
Тест новостей
+
Если вы видите эту страницу, значит маршрутизация работает!
+
+
+
+
+```
+
+### Контакты:
+
+Если проблема не решается, проверьте:
+
+- Версию Node.js (должна быть 16+)
+- Версию npm (должна быть 8+)
+- Наличие всех зависимостей (`npm install`)
diff --git a/FIX_PROCESS_ERROR.md b/FIX_PROCESS_ERROR.md
new file mode 100644
index 0000000..6f01b64
--- /dev/null
+++ b/FIX_PROCESS_ERROR.md
@@ -0,0 +1,48 @@
+# Исправление ошибки "process is not defined"
+
+## Проблема
+
+В консоли браузера появляется ошибка:
+
+```
+[Vue Router warn]: Unexpected error when starting the router: ReferenceError: process is not defined at api.js:3:15
+```
+
+## Причина
+
+В браузере нет объекта `process`, который используется для доступа к переменным окружения Node.js. В Vite нужно использовать `import.meta.env` вместо `process.env`.
+
+## Решение
+
+Ошибка уже исправлена в файле `src/config/api.js`:
+
+**Было:**
+
+```javascript
+BASE_URL: process.env.VUE_APP_API_URL || 'https://api.konturai.kz',
+```
+
+**Стало:**
+
+```javascript
+BASE_URL: 'https://api.konturai.kz',
+```
+
+## Дополнительные исправления
+
+1. **Добавлен Chart.js в main.js** для корректной работы графиков
+2. **Зарегистрирован Chart как глобальный компонент**
+3. **Исправлены все импорты PrimeVue компонентов**
+
+## Проверка
+
+1. Перезапустите сервер разработки: `npm run dev`
+2. Обновите страницу в браузере (Ctrl+Shift+R)
+3. Проверьте консоль - ошибка должна исчезнуть
+4. Попробуйте перейти на страницы новостей
+
+## Если проблема остается
+
+1. Очистите кэш браузера
+2. Удалите папку `node_modules` и выполните `npm install`
+3. Перезапустите сервер разработки
diff --git a/FRONTEND_API_GUIDE.md b/FRONTEND_API_GUIDE.md
new file mode 100644
index 0000000..fc26e93
--- /dev/null
+++ b/FRONTEND_API_GUIDE.md
@@ -0,0 +1,1044 @@
+# Руководство по 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": "Все парсеры доступны"
+ }
+}
+```
+
+---
+
+## 🚀 Примеры использования для фронтенда
+
+### React/JavaScript примеры
+
+```javascript
+// Сервис для работы с API
+class NewsApiService {
+ constructor(baseUrl = '') {
+ this.baseUrl = baseUrl;
+ }
+
+ // Получить новости с фильтрацией
+ async getNews(filters = {}) {
+ const params = new URLSearchParams();
+
+ if (filters.sourceName) params.append('sourceName', filters.sourceName);
+ if (filters.startDate) params.append('startDate', filters.startDate);
+ if (filters.endDate) params.append('endDate', filters.endDate);
+ if (filters.page !== undefined) params.append('page', filters.page);
+ if (filters.size) params.append('size', filters.size);
+ if (filters.sort) params.append('sort', filters.sort);
+
+ const response = await fetch(`${this.baseUrl}/api/items?${params}`);
+ return response.json();
+ }
+
+ // Получить статистику
+ async getStats() {
+ const response = await fetch(`${this.baseUrl}/api/items/stats`);
+ return response.json();
+ }
+
+ // Получить источники
+ async getSources() {
+ const response = await fetch(`${this.baseUrl}/api/items/sources`);
+ return response.json();
+ }
+
+ // Получить источники с обработкой ошибок
+ async getSourcesWithErrorHandling() {
+ try {
+ const response = await fetch(`${this.baseUrl}/api/items/sources`);
+ const result = await response.json();
+
+ if (!result.success) {
+ throw new Error(result.message || 'Ошибка при получении источников');
+ }
+
+ return result.data; // Возвращаем только массив источников
+ } catch (error) {
+ console.error('Ошибка при получении источников:', error);
+ return []; // Возвращаем пустой массив в случае ошибки
+ }
+ }
+
+ // Запустить парсер (админ функция)
+ async runParser(sourceName) {
+ const response = await fetch(`${this.baseUrl}/api/admin/parsers/parse/${sourceName}`, {
+ method: 'POST'
+ });
+ return response.json();
+ }
+
+ // Запустить все парсеры (админ функция)
+ async runAllParsers() {
+ const response = await fetch(`${this.baseUrl}/api/admin/parsers/parse/all`, {
+ method: 'POST'
+ });
+ return response.json();
+ }
+
+ // Проверить состояние системы
+ async getHealthStatus() {
+ const response = await fetch(`${this.baseUrl}/api/health`);
+ return response.json();
+ }
+}
+
+// Использование
+const apiService = new NewsApiService();
+
+// Получить последние новости
+const news = await apiService.getNews({
+ page: 0,
+ size: 20,
+ sort: 'publishedAt,desc'
+});
+
+// Получить новости от конкретного источника
+const kursivNews = await apiService.getNews({
+ sourceName: 'Kursiv Media',
+ page: 0,
+ size: 10
+});
+
+// Получить новости за последнюю неделю
+const weekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
+const recentNews = await apiService.getNews({
+ startDate: weekAgo,
+ sort: 'publishedAt,desc'
+});
+
+// Получить список источников для фильтра
+const sources = await apiService.getSourcesWithErrorHandling();
+console.log('Доступные источники:', sources);
+
+// Получить новости от конкретного источника
+if (sources.length > 0) {
+ const newsFromFirstSource = await apiService.getNews({
+ sourceName: sources[0],
+ page: 0,
+ size: 10
+ });
+}
+```
+
+### Vue.js примеры
+
+```javascript
+// composable для работы с API
+export function useNewsApi() {
+ const baseUrl = '';
+
+ const getNews = async (filters = {}) => {
+ const params = new URLSearchParams();
+ Object.entries(filters).forEach(([key, value]) => {
+ if (value !== undefined && value !== null) {
+ params.append(key, value);
+ }
+ });
+
+ const response = await fetch(`${baseUrl}/api/items?${params}`);
+ return response.json();
+ };
+
+ const getStats = async () => {
+ const response = await fetch(`${baseUrl}/api/items/stats`);
+ return response.json();
+ };
+
+ const getSources = async () => {
+ const response = await fetch(`${baseUrl}/api/items/sources`);
+ return response.json();
+ };
+
+ const getSourcesList = async () => {
+ try {
+ const response = await fetch(`${baseUrl}/api/items/sources`);
+ const result = await response.json();
+ return result.success ? result.data : [];
+ } catch (error) {
+ console.error('Ошибка при получении источников:', error);
+ return [];
+ }
+ };
+
+ return {
+ getNews,
+ getStats,
+ getSources,
+ getSourcesList
+ };
+}
+```
+
+---
+
+## 🎯 Практические примеры использования эндпоинта источников
+
+### 1. Создание динамического фильтра источников
+
+```javascript
+// React компонент для фильтрации по источникам
+import React, { useState, useEffect } from 'react';
+
+function NewsFilter({ onFilterChange }) {
+ const [sources, setSources] = useState([]);
+ const [selectedSource, setSelectedSource] = useState('');
+
+ useEffect(() => {
+ // Загружаем список источников при монтировании компонента
+ fetch('/api/items/sources')
+ .then((res) => res.json())
+ .then((data) => {
+ if (data.success) {
+ setSources(data.data);
+ }
+ })
+ .catch((err) => console.error('Ошибка загрузки источников:', err));
+ }, []);
+
+ const handleSourceChange = (source) => {
+ setSelectedSource(source);
+ onFilterChange({ sourceName: source || undefined });
+ };
+
+ return (
+
+
+
+
+ );
+}
+
+export default NewsFilter;
+```
+
+### 2. Отображение статистики по источникам
+
+```javascript
+// Vue.js компонент для отображения статистики
+
+
+
Статистика по источникам
+
Загрузка...
+
+
+ {{ source }}
+ {{ getSourceCount(source) }}
+
+
+
+
+
+
+```
+
+### 3. Автодополнение для поиска по источникам
+
+```javascript
+// Angular сервис для автодополнения
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Observable, BehaviorSubject } from 'rxjs';
+import { map, catchError } from 'rxjs/operators';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class SourceService {
+ private sourcesSubject = new BehaviorSubject([]);
+ public sources$ = this.sourcesSubject.asObservable();
+
+ constructor(private http: HttpClient) {
+ this.loadSources();
+ }
+
+ private loadSources(): void {
+ this.http.get('/api/items/sources')
+ .pipe(
+ map(response => response.success ? response.data : []),
+ catchError(error => {
+ console.error('Ошибка загрузки источников:', error);
+ return [];
+ })
+ )
+ .subscribe(sources => {
+ this.sourcesSubject.next(sources);
+ });
+ }
+
+ searchSources(query: string): Observable {
+ return this.sources$.pipe(
+ map(sources =>
+ sources.filter(source =>
+ source.toLowerCase().includes(query.toLowerCase())
+ )
+ )
+ );
+ }
+
+ getSources(): Observable {
+ return this.sources$;
+ }
+}
+
+// Компонент автодополнения
+@Component({
+ selector: 'app-source-autocomplete',
+ template: `
+
+
+
0" class="suggestions">
+
+ {{ source }}
+
+
+
+ `
+})
+export class SourceAutocompleteComponent {
+ searchQuery = '';
+ filteredSources: string[] = [];
+
+ constructor(private sourceService: SourceService) {}
+
+ onSearch(query: string): void {
+ if (query.length > 0) {
+ this.sourceService.searchSources(query)
+ .subscribe(sources => {
+ this.filteredSources = sources;
+ });
+ } else {
+ this.filteredSources = [];
+ }
+ }
+
+ selectSource(source: string): void {
+ this.searchQuery = source;
+ this.filteredSources = [];
+ // Эмитим событие выбора источника
+ }
+}
+```
+
+### 4. Кэширование списка источников
+
+```javascript
+// Сервис с кэшированием для React
+class SourceCacheService {
+ constructor() {
+ this.cache = null;
+ this.cacheTime = null;
+ this.cacheTimeout = 5 * 60 * 1000; // 5 минут
+ }
+
+ async getSources() {
+ // Проверяем, есть ли актуальный кэш
+ if (this.cache && this.cacheTime && Date.now() - this.cacheTime < this.cacheTimeout) {
+ return this.cache;
+ }
+
+ try {
+ const response = await fetch('/api/items/sources');
+ const result = await response.json();
+
+ if (result.success) {
+ this.cache = result.data;
+ this.cacheTime = Date.now();
+ return result.data;
+ } else {
+ throw new Error(result.message);
+ }
+ } catch (error) {
+ console.error('Ошибка загрузки источников:', error);
+ // Возвращаем кэш, если есть, даже если он устарел
+ return this.cache || [];
+ }
+ }
+
+ clearCache() {
+ this.cache = null;
+ this.cacheTime = null;
+ }
+}
+
+// Использование в React хуке
+function useSources() {
+ const [sources, setSources] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ const sourceService = new SourceCacheService();
+
+ sourceService
+ .getSources()
+ .then((data) => {
+ setSources(data);
+ setLoading(false);
+ })
+ .catch((err) => {
+ setError(err.message);
+ setLoading(false);
+ });
+ }, []);
+
+ return { sources, loading, error };
+}
+```
+
+---
+
+## 📝 Важные замечания
+
+1. **Пагинация**: Все эндпоинты для получения данных поддерживают пагинацию через параметры `page` и `size`.
+
+2. **Сортировка**: По умолчанию новости сортируются по дате публикации (новые сначала). Можно изменить через параметр `sort`.
+
+3. **Фильтрация**: Поддерживается фильтрация по источнику и диапазону дат.
+
+4. **Формат дат**: Используйте ISO 8601 формат для дат (например: "2024-01-15T10:30:00").
+
+5. **Обработка ошибок**: Все ответы содержат поле `success` для проверки успешности операции.
+
+6. **Административные функции**: Эндпоинты `/api/admin/parsers/*` предназначены для административных функций.
+
+7. **Мониторинг**: Используйте эндпоинты `/api/health/*` для проверки состояния системы.
+
+---
+
+## 🔄 Миграция с старого API
+
+Если у вас был код, использующий старый `ParserController`, вот соответствие эндпоинтов:
+
+| Старый эндпоинт | Новый эндпоинт |
+| ---------------------------------- | ----------------------------------------- |
+| `POST /api/parser/parse/kursiv` | `POST /api/admin/parsers/parse/kursiv` |
+| `POST /api/parser/parse/kapital` | `POST /api/admin/parsers/parse/kapital` |
+| `POST /api/parser/parse/lsm` | `POST /api/admin/parsers/parse/lsm` |
+| `POST /api/parser/parse/rbc` | `POST /api/admin/parsers/parse/rbc` |
+| `POST /api/parser/parse/vedomosti` | `POST /api/admin/parsers/parse/vedomosti` |
+| `POST /api/parser/parse/all` | `POST /api/admin/parsers/parse/all` |
+| `GET /api/parser/items` | `GET /api/items` |
+| `GET /api/parser/stats` | `GET /api/items/stats` |
+| `GET /api/parser/health` | `GET /api/health` |
+| `GET /api/parser/health/mongodb` | `GET /api/health/mongodb` |
+| `GET /api/parser/scheduler/info` | `GET /api/health/scheduler` |
diff --git a/NEWS_README.md b/NEWS_README.md
new file mode 100644
index 0000000..9334421
--- /dev/null
+++ b/NEWS_README.md
@@ -0,0 +1,183 @@
+# Новостной интерфейс для KonturAI
+
+Этот модуль реализует полноценный интерфейс для работы с новостным парсером согласно API документации.
+
+## 🚀 Возможности
+
+### 📰 Лента новостей (`/news`)
+
+- **Отображение новостей** с заголовком, источником, датой и превью текста
+- **Пагинация** с поддержкой "бесконечной прокрутки" и классической пагинации
+- **Сортировка** по дате публикации, дате добавления, заголовку и источнику
+- **Фильтрация** по источнику и датам
+- **Быстрые фильтры** (сегодня, неделя, месяц)
+- **Статистика** на странице с количеством новостей и активных фильтров
+
+### 📊 Дашборд статистики (`/news/dashboard`)
+
+- **Общая статистика** с количеством новостей, источников и средним количеством на источник
+- **Статус системы** с проверкой состояния сервиса и MongoDB
+- **Круговая диаграмма** распределения новостей по источникам
+- **Детальная статистика** по каждому источнику
+
+### ⚙️ Админ-панель (`/admin/news`)
+
+- **Управление парсером** с кнопками запуска парсинга
+- **Мониторинг состояния** сервиса и базы данных
+- **Результаты парсинга** с отображением обработанных новостей
+- **Статистика в реальном времени**
+
+### 📄 Детальный просмотр новости (`/news/:id`)
+
+- **Полный текст** новости с форматированием
+- **Аналитика** (краткое содержание, тональность, теги, сущности)
+- **Техническая информация** (ID, URL, хеш, даты)
+- **Действия** (открыть оригинал, поделиться, вернуться)
+
+## 🛠 Технические детали
+
+### Структура файлов
+
+```
+src/
+├── service/
+│ └── NewsService.js # Сервис для работы с API
+├── config/
+│ └── api.js # Конфигурация API
+├── components/news/
+│ └── NewsFilters.vue # Компонент фильтров
+└── views/news/
+ ├── NewsList.vue # Лента новостей
+ ├── NewsDashboard.vue # Дашборд статистики
+ ├── NewsAdmin.vue # Админ-панель
+ └── NewsDetail.vue # Детальный просмотр
+```
+
+### API Endpoints
+
+- `GET /api/parser/items` - получение новостей с фильтрацией и пагинацией
+- `GET /api/parser/stats` - статистика системы
+- `GET /api/parser/health` - статус сервиса
+- `GET /api/parser/health/mongodb` - статус MongoDB
+- `POST /api/parser/parse/all` - запуск парсинга всех источников
+- `POST /api/parser/parse/{source}` - запуск парсинга конкретного источника
+
+### Конфигурация
+
+Создайте файл `.env` в корне проекта:
+
+```env
+VUE_APP_API_URL=https://api.konturai.kz
+```
+
+## 🎨 UI/UX особенности
+
+### Дизайн
+
+- **Современный интерфейс** с использованием PrimeVue компонентов
+- **Адаптивная верстка** для всех устройств
+- **Цветовая схема** с интуитивными индикаторами статуса
+- **Анимации и переходы** для улучшения пользовательского опыта
+
+### Интерактивность
+
+- **Hover эффекты** на карточках новостей
+- **Загрузочные индикаторы** для всех асинхронных операций
+- **Уведомления** об успешных операциях и ошибках
+- **Tooltips** для дополнительной информации
+
+### Производительность
+
+- **Ленивая загрузка** компонентов
+- **Кэширование** данных статистики
+- **Оптимизированные запросы** с минимальным количеством API вызовов
+
+## 🔧 Настройка и запуск
+
+1. **Установите зависимости:**
+
+ ```bash
+ npm install
+ ```
+
+2. **Настройте API URL:**
+
+ ```bash
+ echo "VUE_APP_API_URL=http://your-api-server:8080" > .env
+ ```
+
+3. **Запустите в режиме разработки:**
+
+ ```bash
+ npm run dev
+ ```
+
+4. **Соберите для продакшена:**
+ ```bash
+ npm run build
+ ```
+
+## 📱 Навигация
+
+### Основное меню
+
+- **Home** → New Dashboard
+- **Новости** → Лента новостей, Статистика
+- **Admin** → Users, Управление парсером
+
+### Маршруты
+
+- `/news` - лента новостей
+- `/news/dashboard` - статистика
+- `/news/:id` - детальный просмотр новости
+- `/admin/news` - управление парсером
+
+## 🎯 Особенности реализации
+
+### Фильтрация
+
+- **Источник**: выпадающий список с динамической загрузкой
+- **Даты**: календарь с поддержкой диапазонов
+- **Сортировка**: поле и направление
+- **Размер страницы**: 10, 20, 50, 100 новостей
+
+### Пагинация
+
+- **Классическая**: кнопки страниц с информацией
+- **Бесконечная**: кнопка "Загрузить еще"
+- **Навигация**: первая, предыдущая, следующая, последняя
+
+### Обработка ошибок
+
+- **Graceful degradation** при недоступности API
+- **Информативные сообщения** об ошибках
+- **Retry механизмы** для критических операций
+
+### Аналитика
+
+- **Chart.js** для визуализации данных
+- **Цветовое кодирование** источников
+- **Процентное соотношение** в статистике
+
+## 🔮 Планы развития
+
+- [ ] **WebSocket** для обновлений в реальном времени
+- [ ] **Поиск** по содержимому новостей
+- [ ] **Избранное** и закладки
+- [ ] **Экспорт** данных в различных форматах
+- [ ] **Уведомления** о новых новостях
+- [ ] **Темная тема** интерфейса
+- [ ] **Мобильное приложение** на базе Capacitor
+
+## 📞 Поддержка
+
+При возникновении проблем:
+
+1. Проверьте конфигурацию API URL
+2. Убедитесь, что бэкенд сервис запущен
+3. Проверьте консоль браузера на наличие ошибок
+4. Обратитесь к документации API
+
+---
+
+**Создано с ❤️ для эффективной работы с новостными данными**
diff --git a/a.md b/a.md
new file mode 100644
index 0000000..edf931c
--- /dev/null
+++ b/a.md
@@ -0,0 +1,351 @@
+# Руководство по API для фронтенда
+
+## Обзор
+
+Это руководство описывает API сервиса `Parser`, который собирает и предоставляет новостные данные. API спроектировано для эффективной работы с клиентскими приложениями и поддерживает пагинацию, сортировку и фильтрацию.
+
+## Реализация в UI: что мы должны показать пользователю
+
+Ниже описаны ключевые элементы интерфейса, которые необходимо создать, используя эндпоинты из этого руководства.
+
+### 1. Основной экран: Лента новостей 📰
+
+Это главный экран приложения, где пользователь видит все собранные новости.
+
+- **Функционал:**
+ - **Отображение новостей:** Используйте `GET /api/parser/items` для загрузки и отображения списка новостей. Каждая новость в списке должна показывать заголовок, источник, дату публикации и краткий текст (`rawText` или `analytics.summary`, когда он будет готов).
+ - **Пагинация:** Так как новостей может быть много, реализуйте "бесконечную прокрутку" (подгрузка новых статей при скролле вниз) или классические кнопки пагинации («1, 2, 3...»). API полностью это поддерживает.
+ - **Сортировка:** По умолчанию новости должны быть отсортированы по дате публикации (`sort=publishedAt,desc`), чтобы пользователь видел сначала самое свежее.
+
+### 2. Панель фильтров 🔎
+
+Сбоку или сверху от ленты новостей должна быть панель, позволяющая пользователю отфильтровать данные.
+
+- **Функционал:**
+ - **Фильтр по источнику:** Добавьте выпадающий список или чекбоксы с названиями источников (например, "Kursiv", "Kapital.kz"). При выборе источника отправляйте его в параметре `sourceName` в запросе к `GET /api/parser/items`.
+ - **Фильтр по дате:** Добавьте два поля для выбора даты ("от" и "до") с календарем. Это позволит пользователю смотреть новости за конкретный период. Используйте параметры `startDate` и `endDate`.
+
+### 3. Дашборд/Статистика 📊
+
+Нужно создать отдельную страницу "Дашборд" или "Статистика", где будет отображаться общая информация о системе.
+
+- **Функционал:**
+ - **Общее количество новостей:** Используйте `GET /api/parser/stats` для получения и отображения общего числа статей в базе (`totalItems`).
+ - **Разбивка по источникам:** На основе поля `sourceStatistics` постройте круговую диаграмму (pie chart), которая наглядно покажет, сколько новостей пришло с каждого источника.
+ - **Статус системы:** Используйте `GET /api/parser/health`, чтобы показать пользователю (скорее, администратору) текущий статус сервиса и планировщика. Можно добавить зелёный/красный индикатор.
+
+### 4. Админ-панель: Управление парсером (опционально, для администраторов) ⚙️
+
+Эта часть интерфейса может быть скрыта от обычных пользователей и доступна только администраторам.
+
+- **Функционал:**
+ - **Ручной запуск парсинга:** Добавьте кнопки "Запустить парсинг Kursiv", "Запустить парсинг Kapital" и "Запустить все". Эти кнопки будут отправлять запросы на эндпоинты `POST /api/parser/parse/{source}` и `POST /api/parser/parse/all`.
+ - **Мониторинг состояния БД:** Используйте `GET /api/parser/health/mongodb` для проверки статуса подключения к базе данных и выводите сообщение об успехе или ошибке.
+
+---
+
+## API Endpoints
+
+### 1. Получение записей с фильтрацией и пагинацией
+
+```http
+GET /api/parser/items?page=0&size=10&sort=publishedAt,desc&sourceName=Kursiv&startDate=2025-01-01&endDate=2025-01-31
+```
+
+**Параметры:**
+
+- `page` (optional, default: 0) - номер страницы
+- `size` (optional, default: 20) - размер страницы
+- `sort` (optional, default: "publishedAt,desc") - сортировка
+- `sourceName` (optional) - фильтр по источнику
+- `startDate` (optional) - начальная дата (ISO format)
+- `endDate` (optional) - конечная дата (ISO format)
+
+**Ответ:**
+
+```json
+{
+ "success": true,
+ "message": "Записи получены успешно",
+ "data": {
+ "content": [
+ {
+ "id": "...",
+ "sourceName": "Kursiv (Бизнес/экономика)",
+ "title": "Заголовок новости",
+ "url": "https://kursiv.media/...",
+ "publishedAt": "2025-01-14T10:30:00",
+ "addedAt": "2025-01-14T12:00:00",
+ "rawText": "Текст статьи...",
+ "hash": "...",
+ "category": "Бизнес",
+ "analytics": {
+ "summary": null,
+ "sentiment": null,
+ "tags": [],
+ "entities": {}
+ }
+ }
+ ],
+ "pageable": {
+ "sort": {
+ "sorted": true,
+ "unsorted": false,
+ "empty": false
+ },
+ "pageNumber": 0,
+ "pageSize": 10,
+ "offset": 0,
+ "paged": true,
+ "unpaged": false
+ },
+ "totalElements": 25,
+ "totalPages": 3,
+ "last": false,
+ "first": true,
+ "numberOfElements": 10,
+ "size": 10,
+ "number": 0,
+ "sort": {
+ "sorted": true,
+ "unsorted": false,
+ "empty": false
+ },
+ "empty": false
+ }
+}
+```
+
+### 2. Статистика системы
+
+```http
+GET /api/parser/stats
+```
+
+**Ответ:**
+
+```json
+{
+ "success": true,
+ "message": "Статистика получена успешно",
+ "data": {
+ "totalItems": 150,
+ "sourceStatistics": {
+ "Kursiv (Бизнес/экономика)": 75,
+ "Kapital.kz (Бизнес)": 75
+ },
+ "lastUpdate": "2025-01-14T12:00:00"
+ }
+}
+```
+
+### 3. Запуск парсинга
+
+#### Парсинг всех источников
+
+```http
+POST /api/parser/parse/all
+```
+
+**Ответ:**
+
+```json
+{
+ "success": true,
+ "message": "Парсинг всех источников завершен успешно",
+ "data": [
+ {
+ "source": "Kursiv",
+ "processedItems": 15,
+ "totalItemsInDb": 90,
+ "status": "completed"
+ },
+ {
+ "source": "Kapital",
+ "processedItems": 12,
+ "totalItemsInDb": 90,
+ "status": "completed"
+ }
+ ]
+}
+```
+
+#### Парсинг конкретного источника
+
+```http
+POST /api/parser/parse/kursiv
+POST /api/parser/parse/kapital
+```
+
+### 4. Проверка состояния
+
+#### Общее состояние
+
+```http
+GET /api/parser/health
+```
+
+**Ответ:**
+
+```json
+{
+ "success": true,
+ "message": "Сервис работает",
+ "data": {
+ "status": "UP",
+ "service": "RSS Parser System",
+ "timestamp": 1705123456789,
+ "scheduler": "Enabled - runs every 30 minutes"
+ }
+}
+```
+
+#### Состояние MongoDB
+
+```http
+GET /api/parser/health/mongodb
+```
+
+**Ответ при успехе:**
+
+```json
+{
+ "success": true,
+ "message": "MongoDB подключение успешно",
+ "data": {
+ "status": "UP",
+ "message": "MongoDB подключение успешно",
+ "timestamp": 1705123456789
+ }
+}
+```
+
+**Ответ при ошибке:**
+
+```json
+{
+ "success": false,
+ "message": "Ошибка подключения к MongoDB",
+ "data": {
+ "status": "DOWN",
+ "message": "Ошибка подключения к MongoDB: Command failed with error 13 (Unauthorized)",
+ "timestamp": 1705123456789,
+ "suggestion": "Проверьте учетные данные в application.properties"
+ }
+}
+```
+
+## Примеры использования
+
+### 1. Получение последних новостей с пагинацией
+
+```javascript
+// Получить первую страницу с 10 записями, отсортированными по дате публикации
+fetch('/api/parser/items?page=0&size=10&sort=publishedAt,desc')
+ .then((response) => response.json())
+ .then((data) => {
+ if (data.success) {
+ const items = data.data.content;
+ const totalPages = data.data.totalPages;
+ // Обработка данных
+ }
+ });
+```
+
+### 2. Фильтрация по источнику
+
+```javascript
+// Получить только новости от Kursiv
+fetch('/api/parser/items?sourceName=Kursiv (Бизнес/экономика)&page=0&size=20')
+ .then((response) => response.json())
+ .then((data) => {
+ if (data.success) {
+ const kursivNews = data.data.content;
+ // Обработка данных
+ }
+ });
+```
+
+### 3. Фильтрация по датам
+
+```javascript
+// Получить новости за последнюю неделю
+const endDate = new Date().toISOString();
+const startDate = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
+
+fetch(`/api/parser/items?startDate=${startDate}&endDate=${endDate}`)
+ .then((response) => response.json())
+ .then((data) => {
+ if (data.success) {
+ const recentNews = data.data.content;
+ // Обработка данных
+ }
+ });
+```
+
+### 4. Комплексная фильтрация
+
+```javascript
+// Получить новости Kapital за последний месяц, отсортированные по дате
+const endDate = new Date().toISOString();
+const startDate = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString();
+
+fetch(`/api/parser/items?sourceName=Kapital.kz (Бизнес)&startDate=${startDate}&endDate=${endDate}&sort=publishedAt,desc&page=0&size=50`)
+ .then((response) => response.json())
+ .then((data) => {
+ if (data.success) {
+ const kapitalNews = data.data.content;
+ const totalCount = data.data.totalElements;
+ // Обработка данных
+ }
+ });
+```
+
+### 5. Получение статистики
+
+```javascript
+fetch('/api/parser/stats')
+ .then((response) => response.json())
+ .then((data) => {
+ if (data.success) {
+ const stats = data.data;
+ console.log(`Всего новостей: ${stats.totalItems}`);
+ console.log('По источникам:', stats.sourceStatistics);
+ }
+ });
+```
+
+## Поддерживаемые форматы дат
+
+API поддерживает следующие форматы дат:
+
+- `yyyy-MM-dd` (например: 2025-01-14)
+- `yyyy-MM-dd'T'HH:mm:ss` (например: 2025-01-14T10:30:00)
+- `yyyy-MM-dd'T'HH:mm:ss.SSS` (например: 2025-01-14T10:30:00.000)
+- `yyyy-MM-dd HH:mm:ss` (например: 2025-01-14 10:30:00)
+
+## Обработка ошибок
+
+Все ответы API следуют единому формату:
+
+```json
+{
+ "success": false,
+ "message": "Описание ошибки",
+ "error": "Детальная информация об ошибке"
+}
+```
+
+HTTP статус коды:
+
+- `200` - Успешный запрос
+- `500` - Внутренняя ошибка сервера
+- `503` - Сервис недоступен (например, MongoDB)
+
+## Рекомендации для фронтенда
+
+1. **Кэширование**: Используйте кэширование для статистики и часто запрашиваемых данных
+2. **Пагинация**: Реализуйте бесконечную прокрутку или традиционную пагинацию
+3. **Фильтры**: Предоставьте пользователю удобные фильтры по источнику и датам
+4. **Обновление**: Используйте WebSocket или polling для обновления данных в реальном времени
+5. **Обработка ошибок**: Всегда проверяйте поле `success` в ответе
diff --git a/docs/auth-api.md b/docs/auth-api.md
index bdc4844..e2ce817 100644
--- a/docs/auth-api.md
+++ b/docs/auth-api.md
@@ -14,7 +14,7 @@ JWT-аутентификация. Публичные и админские эн
### Базовый URL
- Прод: укажите боевой домен/порт
-- Dev локально: `http://localhost:8080`
+- Dev локально: `https://api.konturai.kz`
### Переменные окружения (frontend)
diff --git a/src/components/news/NewsFilters.vue b/src/components/news/NewsFilters.vue
new file mode 100644
index 0000000..cd381d9
--- /dev/null
+++ b/src/components/news/NewsFilters.vue
@@ -0,0 +1,220 @@
+
+
+
+
+
+ Фильтры
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/config/api.js b/src/config/api.js
new file mode 100644
index 0000000..4ba7235
--- /dev/null
+++ b/src/config/api.js
@@ -0,0 +1,36 @@
+// API Configuration
+export const API_CONFIG = {
+ BASE_URL: 'https://api.konturai.kz',
+ ENDPOINTS: {
+ // MarketItemController - Получение данных
+ NEWS_ITEMS: '/api/parser/items',
+ NEWS_ITEM_BY_ID: '/api/parser/items',
+ STATS: '/api/parser/items/stats',
+ SOURCES: '/api/parser/items/sources',
+ NEWS_BY_SOURCE: '/api/parser/items/source',
+
+ // ParserAdminController - Управление парсерами
+ PARSERS: '/api/parser/admin/parsers',
+ PARSERS_INFO: '/api/parser/admin/parsers/info',
+ PARSER_EXISTS: '/api/parser/admin/parsers/exists',
+ PARSE_ALL: '/api/parser/admin/parsers/parse/all',
+ PARSE_SOURCE: '/api/parser/admin/parsers/parse',
+
+ // HealthCheckController - Мониторинг системы
+ HEALTH: '/api/parser/health',
+ MONGO_HEALTH: '/api/parser/health/mongodb',
+ SCHEDULER_HEALTH: '/api/parser/health/scheduler',
+ SYSTEM_HEALTH: '/api/parser/health/system',
+ PARSERS_HEALTH: '/api/parser/health/parsers'
+ }
+};
+
+// Default request configuration
+export const DEFAULT_REQUEST_CONFIG = {
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+};
+
+// Request timeout (in milliseconds)
+export const REQUEST_TIMEOUT = 30000;
diff --git a/src/layout/AppMenu.vue b/src/layout/AppMenu.vue
index 1a8af0d..7e55bc3 100644
--- a/src/layout/AppMenu.vue
+++ b/src/layout/AppMenu.vue
@@ -11,9 +11,19 @@ const model = ref([
{ label: 'New Dashboard', icon: 'pi pi-fw pi-home', to: '/dashboard-new' }
]
},
+ {
+ label: 'Новости',
+ items: [
+ { label: 'Лента новостей', icon: 'pi pi-align-left', to: '/news' },
+ { label: 'Статистика', icon: 'pi pi-fw pi-chart-bar', to: '/news/dashboard' }
+ ]
+ },
{
label: 'Admin',
- items: [{ label: 'Users', icon: 'pi pi-fw pi-users', to: '/admin/users' }]
+ items: [
+ { label: 'Users', icon: 'pi pi-fw pi-users', to: '/admin/users' },
+ { label: 'Управление парсером', icon: 'pi pi-fw pi-cog', to: '/admin/news' }
+ ]
}
// {
// label: 'UI Components',
diff --git a/src/router/index.js b/src/router/index.js
index 881b3c3..883b301 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -131,6 +131,24 @@ const router = createRouter({
name: 'documentation',
component: () => import('@/views/pages/Documentation.vue'),
meta: { requiresAuth: true }
+ },
+ {
+ path: '/news',
+ name: 'news',
+ component: () => import('@/views/news/NewsList.vue'),
+ meta: { requiresAuth: true }
+ },
+ {
+ path: '/news/dashboard',
+ name: 'news-dashboard',
+ component: () => import('@/views/news/NewsDashboard.vue'),
+ meta: { requiresAuth: true }
+ },
+ {
+ path: '/news/:id',
+ name: 'news-detail',
+ component: () => import('@/views/news/NewsDetail.vue'),
+ meta: { requiresAuth: true }
}
]
},
@@ -143,6 +161,12 @@ const router = createRouter({
name: 'admin-users',
component: () => import('@/views/pages/AdminUsers.vue'),
meta: { requiresAuth: true, requiresAdmin: true }
+ },
+ {
+ path: 'news',
+ name: 'admin-news',
+ component: () => import('@/views/news/NewsAdmin.vue'),
+ meta: { requiresAuth: true, requiresAdmin: true }
}
]
},
diff --git a/src/service/AuthService.js b/src/service/AuthService.js
index 816bdb9..4969cfc 100644
--- a/src/service/AuthService.js
+++ b/src/service/AuthService.js
@@ -1,6 +1,7 @@
// Lightweight auth service to manage tokens and API calls
-const API_URL = import.meta.env.VITE_API_URL || 'https://api.konturai.kz';
+// Use proxy in development, direct API in production
+const API_URL = import.meta.env.VITE_API_URL || (import.meta.env.DEV ? '' : 'https://api.konturai.kz');
const TOKEN_STORAGE_KEY = 'auth.accessToken';
const REFRESH_STORAGE_KEY = 'auth.refreshToken';
@@ -65,6 +66,15 @@ async function logout() {
clearTokens();
}
+async function readErrorMessage(response) {
+ try {
+ const data = await response.json();
+ return data.message || `HTTP ${response.status}: ${response.statusText}`;
+ } catch (_) {
+ return `HTTP ${response.status}: ${response.statusText}`;
+ }
+}
+
async function authFetch(input, init = {}) {
const headers = new Headers(init.headers || {});
const token = getAccessToken();
@@ -91,6 +101,7 @@ export default {
refreshToken,
logout,
authFetch,
+ readErrorMessage,
getAccessToken,
getRefreshToken,
setTokens,
diff --git a/src/service/NewsService.js b/src/service/NewsService.js
new file mode 100644
index 0000000..0e84f55
--- /dev/null
+++ b/src/service/NewsService.js
@@ -0,0 +1,325 @@
+import { API_CONFIG, DEFAULT_REQUEST_CONFIG } from '@/config/api';
+import AuthService from './AuthService';
+
+const API_BASE_URL = API_CONFIG.BASE_URL;
+
+class NewsService {
+ // ===== MarketItemController - Получение данных =====
+
+ async getNewsItems(params = {}) {
+ const { page = 0, size = 20, sort = 'publishedAt,desc', sourceName = null, startDate = null, endDate = null } = params;
+
+ const queryParams = new URLSearchParams({
+ page: page.toString(),
+ size: size.toString(),
+ sort
+ });
+
+ if (sourceName) {
+ queryParams.append('sourceName', sourceName);
+ }
+ if (startDate) {
+ queryParams.append('startDate', startDate);
+ }
+ if (endDate) {
+ queryParams.append('endDate', endDate);
+ }
+
+ try {
+ const response = await AuthService.authFetch(`${API_BASE_URL}${API_CONFIG.ENDPOINTS.NEWS_ITEMS}?${queryParams}`);
+ const data = await response.json();
+
+ if (!response.ok) {
+ throw new Error(data.message || 'Ошибка при загрузке новостей');
+ }
+
+ return data;
+ } catch (error) {
+ console.error('Ошибка при загрузке новостей:', error);
+ throw error;
+ }
+ }
+
+ async getNewsItemById(id) {
+ try {
+ const response = await AuthService.authFetch(`${API_BASE_URL}${API_CONFIG.ENDPOINTS.NEWS_ITEM_BY_ID}/${id}`);
+ const data = await response.json();
+
+ if (!response.ok) {
+ throw new Error(data.message || 'Ошибка при загрузке новости');
+ }
+
+ return data;
+ } catch (error) {
+ console.error('Ошибка при загрузке новости:', error);
+ throw error;
+ }
+ }
+
+ async getNewsBySource(sourceName, params = {}) {
+ const { page = 0, size = 20, sort = 'publishedAt,desc' } = params;
+
+ const queryParams = new URLSearchParams({
+ page: page.toString(),
+ size: size.toString(),
+ sort
+ });
+
+ try {
+ const response = await AuthService.authFetch(`${API_BASE_URL}${API_CONFIG.ENDPOINTS.NEWS_BY_SOURCE}/${encodeURIComponent(sourceName)}?${queryParams}`);
+ const data = await response.json();
+
+ if (!response.ok) {
+ throw new Error(data.message || 'Ошибка при загрузке новостей источника');
+ }
+
+ return data;
+ } catch (error) {
+ console.error('Ошибка при загрузке новостей источника:', error);
+ throw error;
+ }
+ }
+
+ async getStats() {
+ try {
+ const response = await AuthService.authFetch(`${API_BASE_URL}${API_CONFIG.ENDPOINTS.STATS}`);
+ const data = await response.json();
+
+ if (!response.ok) {
+ throw new Error(data.message || 'Ошибка при загрузке статистики');
+ }
+
+ return data;
+ } catch (error) {
+ console.error('Ошибка при загрузке статистики:', error);
+ throw error;
+ }
+ }
+
+ async getSources() {
+ try {
+ const response = await AuthService.authFetch(`${API_BASE_URL}${API_CONFIG.ENDPOINTS.SOURCES}`);
+ const data = await response.json();
+
+ if (!response.ok) {
+ throw new Error(data.message || 'Ошибка при загрузке списка источников');
+ }
+
+ return data;
+ } catch (error) {
+ console.error('Ошибка при загрузке списка источников:', error);
+ throw error;
+ }
+ }
+
+ // ===== ParserAdminController - Управление парсерами =====
+
+ async getParsers() {
+ try {
+ const response = await AuthService.authFetch(`${API_BASE_URL}${API_CONFIG.ENDPOINTS.PARSERS}`);
+ const data = await response.json();
+
+ if (!response.ok) {
+ throw new Error(data.message || 'Ошибка при получении списка парсеров');
+ }
+
+ return data;
+ } catch (error) {
+ console.error('Ошибка при получении списка парсеров:', error);
+ throw error;
+ }
+ }
+
+ async getParsersInfo() {
+ try {
+ const response = await AuthService.authFetch(`${API_BASE_URL}${API_CONFIG.ENDPOINTS.PARSERS_INFO}`);
+ const data = await response.json();
+
+ if (!response.ok) {
+ throw new Error(data.message || 'Ошибка при получении информации о парсерах');
+ }
+
+ return data;
+ } catch (error) {
+ console.error('Ошибка при получении информации о парсерах:', error);
+ throw error;
+ }
+ }
+
+ async checkParserExists(sourceName) {
+ try {
+ const response = await AuthService.authFetch(`${API_BASE_URL}${API_CONFIG.ENDPOINTS.PARSER_EXISTS}/${sourceName}`);
+ const data = await response.json();
+
+ if (!response.ok) {
+ throw new Error(data.message || 'Ошибка при проверке существования парсера');
+ }
+
+ return data;
+ } catch (error) {
+ console.error('Ошибка при проверке существования парсера:', error);
+ throw error;
+ }
+ }
+
+ // ===== HealthCheckController - Мониторинг системы =====
+
+ async getHealth() {
+ try {
+ const response = await AuthService.authFetch(`${API_BASE_URL}${API_CONFIG.ENDPOINTS.HEALTH}`);
+ const data = await response.json();
+
+ if (!response.ok) {
+ throw new Error(data.message || 'Ошибка при проверке состояния сервиса');
+ }
+
+ return data;
+ } catch (error) {
+ console.error('Ошибка при проверке состояния сервиса:', error);
+ throw error;
+ }
+ }
+
+ async getMongoHealth() {
+ try {
+ const response = await AuthService.authFetch(`${API_BASE_URL}${API_CONFIG.ENDPOINTS.MONGO_HEALTH}`);
+ const data = await response.json();
+
+ if (!response.ok) {
+ throw new Error(data.message || 'Ошибка при проверке состояния MongoDB');
+ }
+
+ return data;
+ } catch (error) {
+ console.error('Ошибка при проверке состояния MongoDB:', error);
+ throw error;
+ }
+ }
+
+ async getSchedulerHealth() {
+ try {
+ const response = await AuthService.authFetch(`${API_BASE_URL}${API_CONFIG.ENDPOINTS.SCHEDULER_HEALTH}`);
+ const data = await response.json();
+
+ if (!response.ok) {
+ throw new Error(data.message || 'Ошибка при проверке состояния планировщика');
+ }
+
+ return data;
+ } catch (error) {
+ console.error('Ошибка при проверке состояния планировщика:', error);
+ throw error;
+ }
+ }
+
+ async getSystemHealth() {
+ try {
+ const response = await AuthService.authFetch(`${API_BASE_URL}${API_CONFIG.ENDPOINTS.SYSTEM_HEALTH}`);
+ const data = await response.json();
+
+ if (!response.ok) {
+ throw new Error(data.message || 'Ошибка при получении информации о системе');
+ }
+
+ return data;
+ } catch (error) {
+ console.error('Ошибка при получении информации о системе:', error);
+ throw error;
+ }
+ }
+
+ async getParsersHealth() {
+ try {
+ const response = await AuthService.authFetch(`${API_BASE_URL}${API_CONFIG.ENDPOINTS.PARSERS_HEALTH}`);
+ const data = await response.json();
+
+ if (!response.ok) {
+ throw new Error(data.message || 'Ошибка при проверке состояния парсеров');
+ }
+
+ return data;
+ } catch (error) {
+ console.error('Ошибка при проверке состояния парсеров:', error);
+ throw error;
+ }
+ }
+
+ // ===== Административные методы парсинга =====
+
+ async parseAll() {
+ try {
+ const response = await AuthService.authFetch(`${API_BASE_URL}${API_CONFIG.ENDPOINTS.PARSE_ALL}`, {
+ method: 'POST',
+ ...DEFAULT_REQUEST_CONFIG
+ });
+ const data = await response.json();
+
+ if (!response.ok) {
+ throw new Error(data.message || 'Ошибка при запуске парсинга всех источников');
+ }
+
+ return data;
+ } catch (error) {
+ console.error('Ошибка при запуске парсинга всех источников:', error);
+ throw error;
+ }
+ }
+
+ async parseSource(source) {
+ try {
+ const response = await AuthService.authFetch(`${API_BASE_URL}${API_CONFIG.ENDPOINTS.PARSE_SOURCE}/${source}`, {
+ method: 'POST',
+ ...DEFAULT_REQUEST_CONFIG
+ });
+ const data = await response.json();
+
+ if (!response.ok) {
+ throw new Error(data.message || `Ошибка при запуске парсинга источника ${source}`);
+ }
+
+ return data;
+ } catch (error) {
+ console.error(`Ошибка при запуске парсинга источника ${source}:`, error);
+ throw error;
+ }
+ }
+
+ // ===== Вспомогательные методы =====
+
+ // Методы для форматирования дат
+ formatDate(dateString) {
+ if (!dateString) return '';
+ const date = new Date(dateString);
+ return date.toLocaleDateString('ru-RU', {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric',
+ hour: '2-digit',
+ minute: '2-digit'
+ });
+ }
+
+ formatDateShort(dateString) {
+ if (!dateString) return '';
+ const date = new Date(dateString);
+ return date.toLocaleDateString('ru-RU', {
+ month: 'short',
+ day: 'numeric',
+ hour: '2-digit',
+ minute: '2-digit'
+ });
+ }
+
+ // Метод для получения списка источников из статистики (fallback)
+ async getSourcesFromStats() {
+ try {
+ const stats = await this.getStats();
+ return Object.keys(stats.data.sourceStatistics || {});
+ } catch (error) {
+ console.error('Ошибка при получении списка источников из статистики:', error);
+ return [];
+ }
+ }
+}
+
+export default new NewsService();
diff --git a/src/views/news/NewsAdmin.vue b/src/views/news/NewsAdmin.vue
new file mode 100644
index 0000000..16364e9
--- /dev/null
+++ b/src/views/news/NewsAdmin.vue
@@ -0,0 +1,498 @@
+
+
+
+
+
Управление парсером
+
+
+
+
+
+
+
+
+
+
+ Статус сервиса
+
+
+
+
+
+
+ Статус:
+
+
+
+ Сервис:
+ {{ health.service }}
+
+
+ Планировщик:
+ {{ health.scheduler }}
+
+
+ Время:
+ {{ formatDate(health.timestamp) }}
+
+
+
+
+
Не удалось загрузить статус сервиса
+
+
+
+
+
+
+
+
+
+ Статус базы данных
+
+
+
+
+
+
+ Статус:
+
+
+
+ Сообщение:
+ {{ mongoHealth.message }}
+
+
+ Время:
+ {{ formatDate(mongoHealth.timestamp) }}
+
+
+
+
+ {{ mongoHealth.suggestion }}
+
+
+
+
+
+
Не удалось загрузить статус базы данных
+
+
+
+
+
+
+
+
+
+
+
+ Запуск парсинга
+
+
+
+
+
+
+
+
+
Все источники
+
Запустить парсинг всех доступных источников
+
+
+
+
+
+
+
+
+
Загрузка источников...
+
+
+
+
+
+
Источники не найдены
+
+
+
+
+
+
{{ source }}
+
Запустить парсинг только {{ source }}
+
+
+
+
+
+
+
+
+
+
+
+
+ Результаты последнего парсинга
+
+
+
+
+
+
+
+
{{ result.source }}
+
+
+
+
+ Обработано:
+ {{ result.processedItems }}
+
+
+ Всего в БД:
+ {{ result.totalItemsInDb }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Текущая статистика
+
+
+
+
+
+
+
+
{{ stats.totalItems || 0 }}
+
Всего новостей
+
+
+
+
+
{{ sourceCount }}
+
Источников
+
+
+
+
+
{{ averagePerSource }}
+
Среднее на источник
+
+
+
+
+
{{ formatDate(stats.lastUpdate) }}
+
Последнее обновление
+
+
+
+
+
Не удалось загрузить статистику
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/news/NewsDashboard.vue b/src/views/news/NewsDashboard.vue
new file mode 100644
index 0000000..90a3158
--- /dev/null
+++ b/src/views/news/NewsDashboard.vue
@@ -0,0 +1,366 @@
+
+
+
+
Статистика новостей
+
+
+
+
+
+
+
+
+
+
+
Всего новостей
+
{{ stats.totalItems || 0 }}
+
+
+
+
+
+
+
+
+
+
Последнее обновление
+
{{ formatDate(stats.lastUpdate) }}
+
+
+
+
+
+
+
+
+
+
Источников
+
{{ sourceCount }}
+
+
+
+
+
+
+
+
+
+
Среднее на источник
+
{{ averagePerSource }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Статус сервиса
+
+
+
+
+
+
+ Сервис:
+
+
+
+ Название:
+ {{ health.service }}
+
+
+ Планировщик:
+ {{ health.scheduler }}
+
+
+ Время:
+ {{ formatDate(health.timestamp) }}
+
+
+
+
+
Не удалось загрузить статус сервиса
+
+
+
+
+
+
+
+
+
+ Статус базы данных
+
+
+
+
+
+
+ Статус:
+
+
+
+ Сообщение:
+ {{ mongoHealth.message }}
+
+
+ Время:
+ {{ formatDate(mongoHealth.timestamp) }}
+
+
+
+
+ {{ mongoHealth.suggestion }}
+
+
+
+
+
+
Не удалось загрузить статус базы данных
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Распределение по источникам
+
+
+
+
+
+
Загрузка статистики...
+
+
+
+
Нет данных по источникам
+
+
+
+
+
+
+
+
+
+
+
+
+ Детальная статистика
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ source }}
+
{{ count }} новостей
+
+
+
+
{{ count }}
+
{{ getPercentage(count) }}%
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/news/NewsDetail.vue b/src/views/news/NewsDetail.vue
new file mode 100644
index 0000000..1d99063
--- /dev/null
+++ b/src/views/news/NewsDetail.vue
@@ -0,0 +1,327 @@
+
+
+
+
+
+
Загрузка новости...
+
+
+
+
+
+
Ошибка загрузки
+
{{ error }}
+
+
+
+
+
+
+
Новость не найдена
+
Запрашиваемая новость не существует или была удалена
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ formatDate(news.publishedAt) }}
+
+
+
+ Добавлено: {{ formatDateShort(news.addedAt) }}
+
+
+
+
{{ news.title }}
+
+
+
+
+
+
+
+
+ Аналитика
+
+
+
+
+
+
Краткое содержание:
+
{{ news.analytics.summary }}
+
+
+
Тональность:
+
+
+
+
+
Сущности:
+
+
+ {{ entity }}
+ ({{ count }})
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Содержание
+
+
+
+
+
+ {{ news.rawText }}
+
+
+
+
Текст новости недоступен
+
+
+
+
+
+
+
+
+
+
+ Техническая информация
+
+
+
+
+
+
+
+ ID:
+ {{ news.id }}
+
+
+
+ Хеш:
+ {{ truncateHash(news.hash) }}
+
+
+
+
+
+
+ Дата публикации:
+ {{ formatDate(news.publishedAt) }}
+
+
+ Дата добавления:
+ {{ formatDate(news.addedAt) }}
+
+
+ Источник:
+ {{ news.sourceName }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/news/NewsList.vue b/src/views/news/NewsList.vue
new file mode 100644
index 0000000..37d83dd
--- /dev/null
+++ b/src/views/news/NewsList.vue
@@ -0,0 +1,310 @@
+
+
+
+
Лента новостей
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Всего новостей
+
{{ totalElements || 0 }}
+
+
+
+
+
+
+
+
+
+
На странице
+
{{ numberOfElements || 0 }}
+
+
+
+
+
+
+
+
+
+
Последнее обновление
+
{{ lastUpdateTime }}
+
+
+
+
+
+
+
+
+
+
Активные фильтры
+
{{ activeFiltersCount }}
+
+
+
+
+
+
+
+
+
+
Загрузка новостей...
+
+
+
+
+
Новости не найдены
+
Попробуйте изменить фильтры или обновить страницу
+
+
+
+
+
+
+
+
+
+
+
+ {{ formatDate(item.publishedAt) }}
+
+
+
+ {{ item.title }}
+
+
+
+
+
+
+ {{ getPreviewText(item.rawText) }}
+
+
+
+
+
+
+ Добавлено: {{ formatDateShort(item.addedAt) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vite.config.mjs b/vite.config.mjs
index dbb18f9..c75a3a7 100644
--- a/vite.config.mjs
+++ b/vite.config.mjs
@@ -20,5 +20,25 @@ export default defineConfig({
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
+ },
+ server: {
+ proxy: {
+ '/api': {
+ target: 'https://api.konturai.kz',
+ changeOrigin: true,
+ secure: true,
+ configure: (proxy, _options) => {
+ proxy.on('error', (err, _req, _res) => {
+ console.log('proxy error', err);
+ });
+ proxy.on('proxyReq', (proxyReq, req, _res) => {
+ console.log('Sending Request to the Target:', req.method, req.url);
+ });
+ proxy.on('proxyRes', (proxyRes, req, _res) => {
+ console.log('Received Response from the Target:', proxyRes.statusCode, req.url);
+ });
+ }
+ }
+ }
}
});