Skip to content

kotru21/cat-api-telegraf

Repository files navigation

🐱 Cat API Telegram Bot & Web Platform

Современное приложение с Telegram ботом и веб-интерфейсом для просмотра и взаимодействия с фотографиями котов через The Cat API.

📋 Содержание

🚀 Особенности

  • 🎲 Случайные коты с детальной информацией о породах
  • ❤️ Система лайков с персонализацией для пользователей
  • 🏆 Лидерборд самых популярных пород
  • 🔍 Умный поиск по характеристикам породы
  • 📱 Dual Interface: Telegram бот + веб-интерфейс
  • 🔄 Real-time обновления через WebSocket
  • 👤 Telegram авторизация в веб-интерфейсе

🧱 Технологический стек

Backend:

  • Bun v1.0+ - JavaScript runtime с встроенным WebSocket
  • Hono v4 - лёгкий веб-фреймворк
  • Telegraf.js v4.11.2 - Telegram Bot Framework
  • Prisma ORM v6.16.2 - типобезопасная работа с БД
  • Awilix v12.0.0 - Dependency Injection

База данных:

  • PostgreSQL (продакшен) / SQLite (dev) через Prisma
  • Redis v5.8.2 - сессии и rate limiting (опционально)

Безопасность:

  • Web Crypto API - криптография (HMAC, nonce)
  • CSRF защита с timing-safe сравнением
  • Rate limiting с Redis/in-memory fallback
  • CSP, CORS, Security Headers

Frontend:

  • Tailwind CSS (local build) - стилизация (ранее использовался CDN, теперь компилируется локально). Для Tailwind v4 рекомендуется установить плагин Vite:

    bun install -d tailwindcss @tailwindcss/vite
  • Vanilla JavaScript - интерактивность

  • WebSocket - real-time обновления

🔧 Установка

Требования

  • Bun v1.0.0 или выше

Локальная установка

  1. Клонируйте репозиторий:

    git clone https://github.com/kotru21/cat-api-telegraf.git
    cd cat-api-telegraf
  2. Установите зависимости:

    bun install
  3. Настройте переменные окружения:

    Скопируйте .env.example в .env и заполните:

    # Обязательные
    CATAPI_KEY=your-cat-api-key-from-thecatapi.com
    SESSION_SECRET=your-strong-random-secret-min-10-chars
    
    # Опциональные (значения по умолчанию)
    PORT=5200
    WEBSITE_URL=http://localhost
    WEB_ENABLED=true
    BOT_ENABLED=false
    BOT_TOKEN=your-telegram-bot-token
    NODE_ENV=development
    DATABASE_URL=file:./prisma/main.db
    
    # Redis (опционально)
    REDIS_ENABLED=false
    # REDIS_URL=redis://localhost:6379
  4. Настройте базу данных:

    bun run prisma:generate
    bun run prisma:migrate:dev
  5. Запустите приложение:

    bun start

Дополнительно: локально можно запускать только сбор CSS (при разработке):

bun run css:watch   # слежение и сбор tailwind.css
bun run css:build   # один раз собрать minified tailwind.css

Получение API ключей

  1. The Cat API ключ:

    • Зарегистрируйтесь на thecatapi.com
    • Получите бесплатный API ключ
    • Добавьте в .env как CATAPI_KEY
  2. Telegram Bot Token:

    • Напишите @BotFather в Telegram
    • Создайте нового бота командой /newbot
    • Получите токен и добавьте в .env как BOT_TOKEN

⚙️ Конфигурация

Переменные окружения

Переменная Тип По умолчанию Описание
CATAPI_KEY string обязательно API ключ The Cat API
PORT number 5200 Порт HTTP/WebSocket сервера
WEBSITE_URL string http://localhost Базовый URL приложения
WEB_ENABLED boolean true Включить веб-сервер
BOT_ENABLED boolean true Включить Telegram бота
BOT_TOKEN string опционально* Токен Telegram бота
SESSION_SECRET string your-secret-key-here Секрет для сессий
NODE_ENV enum development Окружение
DATABASE_URL string file:./prisma/main.db Строка подключения к БД
REDIS_URL string опционально** URL Redis для сессий

* Обязательно, если BOT_ENABLED=true
** Обязательно в продакшене

🗄️ База данных

Приложение использует Prisma ORM с PostgreSQL в продакшене и SQLite для разработки.

Автоматическая инициализация таблиц

При запуске npm start или bun start приложение автоматически:

Development (SQLite):

  • Копирует schema.sqlite.prismaschema.prisma
  • Генерирует Prisma Client
  • Создаёт таблицы напрямую через SQL (если их нет)

Production (PostgreSQL):

  • Копирует schema.postgres.prismaschema.prisma
  • Генерирует Prisma Client
  • Применяет миграции из prisma/migrations/ через prisma migrate deploy

⚠️ Важно для Production: Перед деплоем убедитесь, что миграции для PostgreSQL созданы и закоммичены в репозиторий.

Команды Prisma

# Генерация клиента после изменения схемы
npm run prisma:generate

# Создание и применение миграций
npm run prisma:migrate:dev

# Запуск Prisma Studio (GUI для БД)
npm run prisma:studio

Если вы запускаете локально в режиме `development` с SQLite (значение `DATABASE_URL=file:./prisma/main.db` в `.env`), можно использовать удобные dev-скрипты:

```bash
# Генерация клиента для sqlite (использует prisma/schema.sqlite.prisma)
npm run prisma:generate:dev

# Создание / применение миграций локально в sqlite (внутри prisma/main.db)
npm run prisma:migrate:dev:sqlite

## 📊 API Документация

### Основные endpoints

```http
GET /api/cat/:id              # Получить кота по ID
GET /api/leaderboard          # Лидерборд популярных пород
GET /api/similar              # Поиск по характеристикам
POST /api/like                # Поставить лайк (требует авторизации)
DELETE /api/like              # Убрать лайк (требует авторизации)
GET /api/profile              # Профиль пользователя (требует авторизации)
POST /api/auth/telegram       # Telegram авторизация

Health checks

GET /healthz                  # Проверка состояния
GET /readyz                   # Проверка готовности

🤖 Telegram Bot

Доступные команды

Команда Описание
/start Приветствие и главное меню
/fact Случайный кот с информацией
/mylikes Список лайкнутых котов
/top Лидерборд популярных пород

Интерактивные действия

  • ❤️ Лайк - поставить/убрать лайк коту
  • 🔍 Похожие - найти котов с похожими характеристиками
  • 📊 Лидерборд - посмотреть популярные породы
  • 🎲 Еще кот - получить еще одного случайного кота

🚀 Развертывание

Production Requirements

# Обязательные для продакшена
NODE_ENV=production
CATAPI_KEY=your-cat-api-key
SESSION_SECRET=complex-random-string-min-32-chars
DATABASE_URL=postgresql://user:pass@host:5432/db

# Redis (рекомендуется в production)
REDIS_ENABLED=true
REDIS_URL=redis://localhost:6379

# Telegram (если нужен бот)
BOT_ENABLED=true
BOT_TOKEN=your-production-bot-token

# Безопасность
WEBSITE_URL=https://yourdomain.com

## 🧩 Frontend Architecture (2025 Refactor)

Фронтенд разделён на прозрачные слои (Clean-ish layering):

```
src/web/public/js/
   api.js                // HTTP helper + caching
   utils.js              // Утилиты и константы (PLACEHOLDER, sanitize, preloadImages)
   core/
      state/
         store.js          // Pub/sub store + event bus
         lifecycle.js      // registerCleanup / runCleanups
      services/           // ONLY: fetch -> normalize -> update store (+ TTL кэш)
         LeaderboardService.js
         LikesService.js
         ProfileService.js
         CatDetailsService.js
      ui/                 // Чистый DOM (render / create... без fetch)
         leaderboard.js
         likes.js
         catDetails.js
         skeleton.js
      errors/
         errorMapper.js
         notify.js         // notifyError / notifySuccess (toast + dedupe)
   components/           // Отдельные переиспользуемые "виджеты"
      searchAndSort.js
      heroAvatars.js
   pages/                // Оркестрация: подписка + вызов сервисов + lifecycle
      indexPage.js
      profilePage.js
      catDetailsPage.js
```

Guidelines / Правила слоя:
1. pages → orchestration only (никакой логики трансформации данных, минимум DOM).
2. services → обращение к `api.js`, нормализация формата, TTL-кэш (Map / timestamps), обновление store.
3. ui → функции create*/render* без сетевых запросов; получают уже нормализованные данные.
4. errors → централизованный mapping + уведомления; исключает дублирование try/catch.
5. a11y → `aria-busy` на контейнерах, `role=list/listitem`, скрытие skeleton через `aria-hidden` (частично внедрено).
6. Никаких inline event handlers в HTML (CSP friendly) — см. раздел Security/CSP.

### Data Model (кратко)
Полное описание: `docs/data-model.md` (создано для синхронизации фронт/бэк). Ниже краткая выжимка:

| Entity            | Поля (нормализованные)                                                                                  |
|-------------------|----------------------------------------------------------------------------------------------------------|
| LeaderboardRow    | `position`, `catId`, `breedName`, `likes`, `change` (резерв), `imageUrl`                                 |
| Like              | `catId`, `breedName`, `imageUrl`, `likes`                                                                |
| CatDetails        | `id`, `breedName`, `description`, `likes`, `wikipediaUrl`, `origin`, `temperament`, `lifeSpan`, `weightMetric`, `weightImperial`, `imageUrl` |
| Profile           | Телеграм данные пользователя (`first_name`, `last_name`, `username`, `photo_url`)                       |

`catId` — единый публичный идентификатор. В переходный период сервис `normalizeRow` имеет fallback цепочку `id || breed_id || cat_id` и выводит предупреждение в dev, если идентификатора нет. План: удалить fallback после выравнивания схемы БД.

### Кэширование (TTL)
- Leaderboard: 15s
- Likes: 10s + отдельный fetch count
- Profile: 30s (см. ProfileService — если будет добавлен TTL)
- CatDetails: 30s (Map cache)

### Тестирование
`jest` + `jsdom` для юнитов (store, нормализация, UI компонентов). Запуск:
```
npm test
```
Покрытие можно расширить тестами на optimistic update (удаление лайка) и поведение skeleton.

### Security / CSP
- Убраны inline обработчики (`onerror`, `onclick`) — заменены на JS привязку.
- Используется Helmet + строгий `script-src-attr 'none'`.
- Fallback изображений через `data-fallback` + JS `error` listener.

### Roadmap / Next
- [ ] Удалить fallback цепочку `breed_id` / `cat_id` → оставить только `id`.
- [ ] Дополнить a11y: скрывать skeleton через `aria-hidden="true"`, live region для обновления лайков.
- [ ] Интегрировать реальный POST лайка на странице деталей (сейчас кнопка локально инкрементирует число).
- [ ] Расширить тесты (optimistic rollback, WebSocket stats stub).
- [ ] Вынести image preloading в общий модуль с абстракцией cancellation.

Heroku Deployment

# Подготовка
heroku create your-app-name
# Использовать MongoDB Atlas (или другой MongoDB add-on) и Heroku Redis
heroku addons:create mongodbatlas    # или другой MongoDB add-on (mongolab/mongoDB Atlas)
heroku addons:create heroku-redis:mini

# Переменные окружения
heroku config:set NODE_ENV=production
heroku config:set CATAPI_KEY=your-key
heroku config:set SESSION_SECRET=your-secret
heroku config:set BOT_TOKEN=your-bot-token
heroku config:set REDIS_ENABLED=true

# Если Mongo add-on устанавливает MONGODB_URI, можно скопировать его в DATABASE_URL:
heroku config:set DATABASE_URL=$(heroku config:get MONGODB_URI -a your-app-name)

# Деплой
git push heroku main

🔗 Links


Если проект был полезен, поставьте звездочку на GitHub!


Changelog (Frontend Refactor Summary)

2025-12: Bun Migration - Миграция с Node.js/Express на Bun/Hono. Redis теперь опционален (REDIS_ENABLED), Web Crypto API вместо Node.js crypto, SESSION_SECRET обязателен.

2025-09: Полный рефактор фронтенда (слои services/state/ui, удалены legacy компоненты, введены тесты, устранены inline handlers, консолидация идентификатора catId).

About

Modern Telegram bot and web platform for exploring cat photos via The Cat API. Features include random cats, breed information, like system, leaderboard, and real-time updates via WebSocket. Built with Node.js, Express, Telegraf, Prisma ORM, and Redis.

Topics

Resources

Stars

Watchers

Forks

Contributors

Languages