Files
2026-08-14 16:42:12 +05:00

32 KiB
Raw Permalink Blame History

Руководство по API для фронтенда

Обзор изменений

После рефакторинга старый ParserController был разделен на три специализированных контроллера для лучшей организации и масштабируемости:

  • MarketItemController (/api/items) - для получения данных
  • ParserAdminController (/api/admin/parsers) - для управления парсерами
  • HealthCheckController (/api/health) - для мониторинга системы

📊 MarketItemController - Получение данных

Базовый URL: /api/items

1. Получение списка новостей с фильтрацией и пагинацией

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")

Примеры запросов:

// Получить все новости
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');

Ответ:

{
  "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

GET /api/items/{id}

Пример:

fetch('/api/items/64f1a2b3c4d5e6f7g8h9i0j1');

Ответ:

{
  "success": false,
  "message": "Функция поиска по ID пока не реализована"
}

3. Получение статистики

GET /api/items/stats

Ответ:

{
  "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)

GET /api/items/sources

Описание: Возвращает список всех уникальных источников новостей, которые есть в базе данных. Этот эндпоинт полезен для:

  • Построения фильтров по источникам
  • Отображения списка доступных источников в UI
  • Динамического создания выпадающих списков

Ответ:

{
  "success": true,
  "message": "Источники получены успешно",
  "data": [
    "Kursiv (Бизнес/экономика)",
    "Kapital.kz (Бизнес)",
    "LSM.kz",
    "РБК",
    "Ведомости"
  ]
}

Примеры использования:

// Получить список всех источников
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 (
    <select onChange={(e) => onSourceChange(e.target.value)}>
      <option value=''>Все источники</option>
      {sources.map((source) => (
        <option key={source} value={source}>
          {source}
        </option>
      ))}
    </select>
  );
}

// Использование в 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. Получение новостей по конкретному источнику

GET /api/items/source/{sourceName}

Пример:

fetch('/api/items/source/Kursiv Media?page=0&size=10');

Ответ: Аналогичен ответу от /api/items, но только с новостями от указанного источника.


⚙️ ParserAdminController - Управление парсерами

Базовый URL: /api/admin/parsers

⚠️ Важно: Эти эндпоинты предназначены для административных функций и могут потребовать аутентификации в будущем.

1. Запуск конкретного парсера

POST /api/admin/parsers/parse/{sourceName}

Доступные источники:

  • kursiv
  • kapital
  • lsm
  • rbc
  • vedomosti

Пример:

// Запустить парсер Kursiv
fetch('/api/admin/parsers/parse/kursiv', { method: 'POST' });

// Запустить парсер РБК
fetch('/api/admin/parsers/parse/rbc', { method: 'POST' });

Ответ:

{
  "success": true,
  "message": "Парсинг kursiv завершен",
  "data": {
    "source": "kursiv",
    "processedItems": 15,
    "totalItemsInDb": 1265,
    "status": "completed"
  }
}

2. Запуск всех парсеров

POST /api/admin/parsers/parse/all

Пример:

fetch('/api/admin/parsers/parse/all', { method: 'POST' });

Ответ:

{
  "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. Получение списка доступных парсеров

GET /api/admin/parsers

Ответ:

{
  "success": true,
  "message": "Список парсеров получен",
  "data": ["kapital", "kursiv", "lsm", "rbc", "vedomosti"]
}

4. Получение информации о парсерах

GET /api/admin/parsers/info

Ответ:

{
  "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. Проверка существования парсера

GET /api/admin/parsers/exists/{sourceName}

Пример:

fetch('/api/admin/parsers/exists/kursiv');

Ответ:

{
  "success": true,
  "message": "Проверка завершена",
  "data": true
}

🔍 HealthCheckController - Мониторинг системы

Базовый URL: /api/health

1. Базовая проверка состояния

GET /api/health

Ответ:

{
  "success": true,
  "message": "Сервис работает",
  "data": {
    "status": "UP",
    "service": "RSS Parser System",
    "timestamp": 1705312500000,
    "details": "Enabled - runs every 30 minutes"
  }
}

2. Проверка подключения к MongoDB

GET /api/health/mongodb

Ответ (успех):

{
  "success": true,
  "message": "MongoDB подключение успешно",
  "data": {
    "status": "UP",
    "service": "MongoDB подключение успешно",
    "timestamp": 1705312500000,
    "details": "Всего записей: 1317"
  }
}

Ответ (ошибка):

{
  "success": false,
  "message": "Ошибка подключения к MongoDB",
  "data": {
    "status": "DOWN",
    "service": "Ошибка подключения к MongoDB: Connection refused",
    "timestamp": 1705312500000,
    "suggestion": "Проверьте учетные данные в application.properties"
  }
}

3. Информация о планировщике

GET /api/health/scheduler

Ответ:

{
  "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. Общая информация о системе

GET /api/health/system

Ответ:

{
  "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. Проверка состояния парсеров

GET /api/health/parsers

Ответ:

{
  "success": true,
  "message": "Проверка парсеров завершена",
  "data": {
    "totalParsers": 5,
    "availableParsers": ["kapital", "kursiv", "lsm", "rbc", "vedomosti"],
    "status": "UP",
    "message": "Все парсеры доступны"
  }
}

6. Проверка доступности RSS-лент

GET /api/health/rss

Описание: Проверяет доступность всех RSS-лент и предоставляет рекомендации по альтернативным URL в случае недоступности.

Ответ:

{
  "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/"
    }
  }
}

Примеры использования:

// Проверка доступности 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 <div>Загрузка...</div>;

  return (
    <div className='rss-status'>
      <h3>Статус RSS-лент</h3>
      <div className={`status ${rssStatus.overallStatus.toLowerCase()}`}>
        {rssStatus.overallStatus === 'UP'
          ? '✅ Все ленты доступны'
          : rssStatus.overallStatus === 'DEGRADED'
          ? '⚠️ Некоторые ленты недоступны'
          : '❌ Ленты недоступны'}
      </div>

      <div className='feeds-list'>
        {Object.entries(rssStatus.accessibilityResults).map(
          ([source, isAccessible]) => (
            <div
              key={source}
              className={`feed-item ${
                isAccessible ? 'accessible' : 'inaccessible'
              }`}
            >
              <span className='source'>{source}</span>
              <span className='status'>{isAccessible ? '✅' : '❌'}</span>
              {!isAccessible && rssStatus.recommendations[source] && (
                <div className='recommendation'>
                  Рекомендация: {rssStatus.recommendations[source]}
                </div>
              )}
            </div>
          )
        )}
      </div>
    </div>
  );
}

🚀 Примеры использования для фронтенда

React/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 примеры

// 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. Создание динамического фильтра источников

// 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 (
    <div className='news-filter'>
      <label htmlFor='source-filter'>Фильтр по источнику:</label>
      <select
        id='source-filter'
        value={selectedSource}
        onChange={(e) => handleSourceChange(e.target.value)}
      >
        <option value=''>Все источники</option>
        {sources.map((source) => (
          <option key={source} value={source}>
            {source}
          </option>
        ))}
      </select>
    </div>
  );
}

export default NewsFilter;

2. Отображение статистики по источникам

// Vue.js компонент для отображения статистики
<template>
  <div class="sources-stats">
    <h3>Статистика по источникам</h3>
    <div v-if="loading" class="loading">Загрузка...</div>
    <div v-else class="stats-grid">
      <div
        v-for="source in sources"
        :key="source"
        class="stat-item"
        @click="filterBySource(source)"
      >
        <span class="source-name">{{ source }}</span>
        <span class="source-count">{{ getSourceCount(source) }}</span>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      sources: [],
      loading: true,
      stats: {}
    }
  },
  async mounted() {
    await this.loadSources();
    await this.loadStats();
    this.loading = false;
  },
  methods: {
    async loadSources() {
      try {
        const response = await fetch('/api/items/sources');
        const result = await response.json();
        if (result.success) {
          this.sources = result.data;
        }
      } catch (error) {
        console.error('Ошибка загрузки источников:', error);
      }
    },
    async loadStats() {
      try {
        const response = await fetch('/api/items/stats');
        const result = await response.json();
        if (result.success) {
          this.stats = result.data.sourceStatistics;
        }
      } catch (error) {
        console.error('Ошибка загрузки статистики:', error);
      }
    },
    getSourceCount(source) {
      return this.stats[source] || 0;
    },
    filterBySource(source) {
      this.$emit('filter-change', { sourceName: source });
    }
  }
}
</script>

3. Автодополнение для поиска по источникам

// 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<string[]>([]);
  public sources$ = this.sourcesSubject.asObservable();

  constructor(private http: HttpClient) {
    this.loadSources();
  }

  private loadSources(): void {
    this.http.get<any>('/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<string[]> {
    return this.sources$.pipe(
      map(sources =>
        sources.filter(source =>
          source.toLowerCase().includes(query.toLowerCase())
        )
      )
    );
  }

  getSources(): Observable<string[]> {
    return this.sources$;
  }
}

// Компонент автодополнения
@Component({
  selector: 'app-source-autocomplete',
  template: `
    <div class="autocomplete">
      <input
        [(ngModel)]="searchQuery"
        (input)="onSearch($event.target.value)"
        placeholder="Поиск источника..."
        class="search-input"
      />
      <div *ngIf="filteredSources.length > 0" class="suggestions">
        <div
          *ngFor="let source of filteredSources"
          (click)="selectSource(source)"
          class="suggestion-item"
        >
          {{ source }}
        </div>
      </div>
    </div>
  `
})
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. Кэширование списка источников

// Сервис с кэшированием для 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