# Parser Zakupok (Парсер Закупок) AdonisJS 6 приложение для автоматического парсинга аукционов с сайта icetrade.by. Система автоматически собирает данные каждые 6 часов, сохраняет в PostgreSQL и отправляет Telegram уведомления по ключевым словам. ## Возможности - 🔄 Автоматический парсинг аукционов каждые 6 часов - 🔍 Поиск по ключевым словам (заголовок, описание, организация) - 📱 Telegram уведомления пользователям - 📊 Логирование всех операций парсинга - 🗄️ PostgreSQL база данных с полной историей - ⚡ Пагинация и rate limiting для стабильности - 🔧 REST API для управления ## Технологический стек - **Framework**: AdonisJS 6 (TypeScript) - **Database**: PostgreSQL + Lucid ORM - **Scheduler**: adonisjs-scheduler - **HTML Parser**: Cheerio - **Validation**: Zod - **Telegram Bot**: Grammy - **Testing**: Japa ## Установка ### Требования - Node.js >= 20.6 - PostgreSQL >= 14 - npm >= 9 ### Шаги установки ```bash # 1. Клонировать репозиторий git clone cd parser-zakupok # 2. Установить зависимости npm install # 3. Скопировать .env файл cp .env.example .env # 4. Настроить переменные окружения nano .env # или любой редактор # 5. Запустить миграции node ace migration:run # 6. Запустить приложение npm run dev ``` ### Настройка .env ```env # Сервер PORT=3333 HOST=localhost NODE_ENV=development APP_KEY= # База данных PostgreSQL DB_HOST=127.0.0.1 DB_PORT=5432 DB_USER=postgres DB_PASSWORD=your_password DB_DATABASE=parser_zakupok # Telegram Bot TELEGRAM_BOT_TOKEN=your_bot_token_from_@BotFather # Опционально LOG_LEVEL=info ``` ## Команды запуска ### Режим разработки ```bash # Запуск с hot reload (рекомендуется) npm run dev # Альтернативный вариант node ace serve --hmr # С watch режимом node ace serve --watch ``` ### Production режим ```bash # 1. Собрать проект npm run build # 2. Запустить миграции node ace migration:run --force # 3. Запустить сервер npm start # или node build/bin/server.js ``` ## Команды базы данных ### Миграции ```bash # Выполнить все pending миграции node ace migration:run # Откатить последний batch node ace migration:rollback # Откатить все миграции node ace migration:rollback --batch=0 # Пересоздать базу (drop + migrate) node ace migration:fresh # Fresh + seed данные node ace migration:fresh --seed ``` ### Создание новых сущностей ```bash # Создать миграцию node ace make:migration create_table_name node ace make:migration add_column_to_table # Создать модель node ace make:model ModelName # Создать модель + миграцию node ace make:model ModelName -m # Создать контроллер node ace make:controller ControllerName ``` ### Database утилиты ```bash # Открыть REPL с доступом к моделям node ace repl # Примеры в REPL: # > await loadModels() # > const Auction = await import('#models/auction') # > await Auction.default.all() ``` ## Команды парсинга ### Основная команда parse:auctions ```bash # Парсинг с дефолтными настройками (до 10 страниц) node ace parse:auctions # Парсинг конкретного количества страниц node ace parse:auctions --pages=5 # Парсинг без отправки уведомлений (только сохранение) node ace parse:auctions --skip-notifications # Парсинг 1 страницы без уведомлений (для тестирования) node ace parse:auctions --pages=1 --skip-notifications # Комбинация опций node ace parse:auctions --pages=20 --skip-notifications ``` **Что делает команда:** 1. Создает запись в `parse_logs` (статус: running) 2. Парсит страницы с icetrade.by с rate limiting (1 сек между запросами) 3. Валидирует данные через Zod схемы 4. Upsert аукционов по `auction_num` (обновляет существующие, создает новые) 5. Ищет совпадения по ключевым словам 6. Создает записи уведомлений для пользователей 7. Обновляет parse_log (статус: completed/failed, статистика) **Выводит:** - Количество найденных аукционов - Количество новых аукционов - Количество обновленных аукционов - Количество созданных уведомлений - Время выполнения - Ошибки (если есть) ## Команды Scheduler ### Управление планировщиком ```bash # Запустить scheduler (production) node ace scheduler:work # Запустить с watch режимом (development) node ace scheduler:work --watch # Показать список всех запланированных задач node ace scheduler:list # Вывод scheduler:list: # ┌────────────────┬──────────────────┬─────────────────┐ # │ Name │ Cron Expression │ Next Run │ # ├────────────────┼──────────────────┼─────────────────┤ # │ parse:auctions │ 0 0 */6 * * * │ 2024-01-15 18:00│ # └────────────────┴──────────────────┴─────────────────┘ ``` ### Настройка расписания Расписание настроено в `start/scheduler.ts`: ```typescript // Текущее: каждые 6 часов (00:00, 06:00, 12:00, 18:00) scheduler.call(async () => { await ace.exec('parse:auctions', []) }).cron('0 0 */6 * * *') // Другие варианты расписания: // .cron('0 0 * * * *') // Каждый час // .cron('0 */30 * * * *') // Каждые 30 минут // .everyFourHours() // Каждые 4 часа // .dailyAt('09:00') // Ежедневно в 9:00 // .twiceDaily(9, 18) // Дважды в день (9:00, 18:00) ``` ### Production deployment scheduler ```bash # Вариант 1: Использовать встроенный scheduler npm start & # Запустить сервер node ace scheduler:work # Запустить scheduler в отдельном процессе # Вариант 2: Использовать PM2 (рекомендуется) pm2 start ecosystem.config.js # Вариант 3: Systemd services # Создать два сервиса: parser-app.service и parser-scheduler.service ``` ## Команды тестирования ```bash # Запустить все тесты npm test # или node ace test # Запустить конкретный файл теста node ace test --files=tests/unit/models/auction.spec.ts # Запустить тесты по паттерну (grep) node ace test --grep="keyword matching" node ace test --grep="scraper" # Запустить только unit тесты node ace test tests/unit # Запустить только functional тесты node ace test tests/functional ``` ## Code Quality команды ```bash # Проверка типов TypeScript npm run typecheck # Линтинг (ESLint) npm run lint # Автофикс линтинга npm run lint -- --fix # Форматирование (Prettier) npm run format # Проверить форматирование без изменений npm run format -- --check ``` ## Telegram Bot команды ```bash # Запустить Telegram бота (когда будет реализовано в Phase 5) node ace telegram:start # С watch режимом node ace telegram:start --watch ``` ### Команды бота для пользователей После запуска бота, пользователи могут использовать: - `/start` - Регистрация в системе - `/addkeyword <слово>` - Добавить ключевое слово - `/keywords` или `/listkeywords` - Список ваших ключевых слов - `/deletekeyword ` - Удалить ключевое слово - `/help` - Справка по командам ## Структура проекта ``` parser-zakupok/ ├── app/ │ ├── controllers/ # HTTP контроллеры │ ├── models/ # Lucid ORM модели │ │ ├── auction.ts │ │ ├── keyword.ts │ │ ├── user.ts │ │ ├── notification.ts │ │ └── parse_log.ts │ ├── services/ # Бизнес-логика │ │ ├── scraper_service.ts # Парсинг icetrade.by │ │ └── notification_service.ts # Обработка уведомлений │ ├── middleware/ # HTTP middleware │ └── validators/ # VineJS валидаторы ├── commands/ # Ace CLI команды │ └── parse_auctions.ts # Команда парсинга ├── config/ # Конфигурация приложения │ ├── app.ts │ ├── database.ts │ └── logger.ts ├── database/ │ └── migrations/ # Database миграции ├── start/ │ ├── routes.ts # Маршруты │ ├── kernel.ts # Middleware регистрация │ ├── scheduler.ts # Настройка scheduler │ └── env.ts # Env validation ├── tests/ # Тесты (Japa) │ ├── unit/ │ └── functional/ ├── docs/ # Документация ├── .env # Environment переменные ├── adonisrc.ts # AdonisJS конфигурация └── package.json ``` ## Import Aliases В проекте настроены удобные алиасы для импортов: ```typescript #controllers/* → ./app/controllers/*.js #models/* → ./app/models/*.js #services/* → ./app/services/*.js #validators/* → ./app/validators/*.js #middleware/* → ./app/middleware/*.js #config/* → ./config/*.js #database/* → ./database/*.js #start/* → ./start/*.js // Пример использования: import Auction from '#models/auction' import ScraperService from '#services/scraper_service' import { DatabaseConfig } from '#config/database' ``` ## Логирование Все операции логируются через Pino logger: ```typescript import logger from '@adonisjs/core/services/logger' // В коде: logger.info('Scraping started') logger.error({ err }, 'Failed to parse page') logger.debug({ count: auctions.length }, 'Auctions scraped') ``` Логи сохраняются в: - `tmp/logs/app.log` (production) - Console output (development) ## Parse Logs в БД Каждый запуск парсинга создает запись в таблице `parse_logs`: ```sql SELECT * FROM parse_logs ORDER BY started_at DESC LIMIT 5; -- Поля: -- id, parse_type, status, started_at, completed_at, -- items_found, items_new, items_updated, items_failed, -- error_message, error_details ``` Полезные запросы: ```sql -- Последние успешные парсинги SELECT * FROM parse_logs WHERE status = 'completed' ORDER BY started_at DESC; -- Статистика по парсингам SELECT COUNT(*) as total_runs, AVG(items_found) as avg_found, AVG(items_new) as avg_new, SUM(items_failed) as total_failed FROM parse_logs WHERE parse_type = 'auction'; -- Неудачные парсинги с ошибками SELECT started_at, error_message FROM parse_logs WHERE status = 'failed' ORDER BY started_at DESC; ``` ## Troubleshooting ### База данных не подключается ```bash # Проверить что PostgreSQL запущен # Linux/Mac: sudo systemctl status postgresql # Windows: # Проверить через Services (services.msc) # Проверить подключение psql -U postgres -h localhost # Создать базу вручную psql -U postgres CREATE DATABASE parser_zakupok; \q ``` ### Ошибки при парсинге ```bash # Проверить логи tail -f tmp/logs/app.log # Проверить parse_logs в БД node ace repl > await loadModels() > const ParseLog = await import('#models/parse_log') > await ParseLog.default.query().orderBy('started_at', 'desc').first() # Запустить парсинг одной страницы для отладки node ace parse:auctions --pages=1 --skip-notifications ``` ### Scheduler не запускается ```bash # Проверить что scheduler провайдер добавлен в adonisrc.ts cat adonisrc.ts | grep scheduler # Проверить список задач node ace scheduler:list # Запустить вручную для проверки node ace parse:auctions ``` ### TypeScript ошибки ```bash # Очистить build и пересобрать rm -rf build/ npm run build # Проверить типы npm run typecheck # Обновить зависимости npm update ``` ## API Endpoints (будут реализованы в Phase 7) ``` GET /api/auctions # Список аукционов (пагинация, фильтры) GET /api/auctions/:id # Детали аукциона GET /api/keywords # Ключевые слова пользователя POST /api/keywords # Добавить ключевое слово DELETE /api/keywords/:id # Удалить ключевое слово GET /api/notifications # История уведомлений ``` ## Docker (будет реализовано в Phase 8) ```bash # Сборка и запуск docker-compose up -d # Просмотр логов docker-compose logs -f app # Остановка docker-compose down ``` ## Roadmap - [x] Phase 1: Project Setup - [x] Phase 2: Database Models & Migrations - [x] Phase 3: ScraperService Implementation - [x] Phase 4: Scheduler Command - [ ] Phase 5: Telegram Bot Integration - [ ] Phase 6: NotificationService Enhancement - [ ] Phase 7: REST API Endpoints - [ ] Phase 8: Docker Deployment ## Contributing 1. Fork репозиторий 2. Создать feature branch (`git checkout -b feature/AmazingFeature`) 3. Commit изменения (`git commit -m 'Add some AmazingFeature'`) 4. Push в branch (`git push origin feature/AmazingFeature`) 5. Открыть Pull Request ## License [MIT License](LICENSE) ## Поддержка При возникновении проблем: 1. Проверьте [документацию](docs/) 2. Создайте [Issue](../../issues) 3. Проверьте существующие Issues ## Полезные ссылки - [AdonisJS Documentation](https://docs.adonisjs.com/) - [Lucid ORM](https://lucid.adonisjs.com/) - [Grammy Telegram Bot](https://grammy.dev/) - [PostgreSQL Documentation](https://www.postgresql.org/docs/)