Skip to content

Latest commit

 

History

63 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Curator AI Assistant

Событийная платформа для кураторов онлайн-обучения. Ученик пишет в сообщество VK, а куратор работает с вопросом в Telegram Mini App: отвечает вручную либо выбирает сообщения диалога, получает AI-черновик, редактирует и принимает или отклоняет его. AI не отправляет текст ученику без решения куратора.

Репозиторий содержит четыре Spring Boot-сервиса, React Mini App, локальную и staging-инфраструктуру, single-host production-профиль, мониторинг, backup и load-test scripts.

Production Mini App использует same-origin proxy: Cloudflare Worker передаёт /api/miniapp/** на HTTPS-origin backend, Caddy маршрутизирует этот путь в tg-connector-service, а production preflight требует URL и allowed origin интерфейса. Порядок настройки описан в production runbook.

Что реализовано

  • приём, проверка и дедупликация VK Callback API;
  • привязка VK-сообществ к кураторам и синхронизация участников;
  • постоянная история диалогов и рабочая очередь по ученикам;
  • ручной ответ без AI-вызова и списания AI-кредитов;
  • ручной выбор 1–20 сообщений одного диалога для AI-контекста;
  • журналируемая генерация через OpenRouter и curator approval;
  • reservation, charge, release и refund внутреннего баланса;
  • идемпотентная доставка ответов и персональных рассылок в VK;
  • каталог учеников, membership/messaging status и кураторские имена;
  • рабочая область Google Таблиц с service account, гибкими заголовками, community-scoped фильтрами и диагностируемым preview;
  • оплата Telegram Stars и административное пополнение;
  • transactional outbox, retries, DLT registry и workflow watchdog;
  • Actuator, Prometheus, Grafana, Alertmanager и PostgreSQL backup/restore.

Рабочая область Google Таблиц

Backend и отдельный mobile-first раздел Mini App уже поддерживают:

  • подключение общего школьного журнала через единый service account без привязки самого файла к VK-сообществу;
  • независимые curator-owned подключения одной таблицы, выбор нескольких листов и ручной выбор строки заголовков;
  • поля по названиям без контракта на буквы колонок, позиции и оформление;
  • community-scoped шаблоны фильтров с AND/OR, EQUALS, NOT_EQUALS, IN, EMPTY и NOT_EMPTY и проверкой совместимости заголовков;
  • bounded чтение до 20 000 строк логическими страницами по 1000 и пакетами до пяти диапазонов, после чего maxPreviewRows ограничивает уже найденные карточки, а не область поиска;
  • сопоставление числового VK ID, id123 и ссылок vk.com/id123 с каталогом выбранного сообщества, а также отдельные диагностики invalid, duplicate, unresolved profile и отсутствия ученика в сообществе;
  • защищённый Telegram initData flow от connection и выбора листа до fields, фильтра и preview; два реальных тестовых журнала проверены только на чтение;
  • миграцию V26 и owner-scoped backend-store для конфигурации VK/отображаемых полей и последнего успешного preview.

Хранилище V26 пока не подключено к HTTP refresh flow: после reload интерфейс ещё использует browser state, а ошибка Google не возвращает последний snapshot. Также остаются запись ячеек с optimistic conflict, создание рассылки из выбранных карточек и финальный production E2E. Актуальный порядок только оставшихся задач находится в architecture backlog, а подробный контракт — в Google Sheets backend contract.

Фоновый polling, синхронизация по расписанию, AI-интерпретация структуры и отдельные парсеры предметов в текущий scope не входят.

Основной workflow

sequenceDiagram
    autonumber
    actor Student as Ученик
    participant VK as VK Connector
    participant Kafka
    participant Flow as Orchestrator
    participant TG as TG Connector
    participant App as Mini App
    participant AI as AI Service
    participant LLM as OpenRouter
    actor Curator as Куратор

    Student->>VK: Сообщение в VK
    VK-->>Kafka: vk-webhook-intake
    Kafka-->>VK: Асинхронное enrichment
    VK-->>Kafka: vk-incoming-messages
    Kafka-->>Flow: Создать workflow
    Flow-->>Kafka: curator-intake-requests
    Kafka-->>TG: Обновить проекцию очереди
    Curator->>App: Открыть диалог

    alt Ручной ответ
        App->>TG: manual-answer
        TG-->>Kafka: curator-intake-decisions
    else AI-черновик
        App->>TG: send-to-ai + выбранные messageIds
        TG-->>Kafka: curator-intake-decisions
        Flow-->>Kafka: reserve credits
        Flow-->>Kafka: ai-generation-commands
        Kafka-->>AI: Стабильная команда
        AI->>LLM: Выбранный контекст
        LLM-->>AI: Черновик + usage.cost
        AI-->>Kafka: ai-generation-results
        Flow-->>Kafka: curator-approval-requests
        Kafka-->>TG: Сохранить draft
        Curator->>App: Изменить и approve/reject
        TG-->>Kafka: curator-decisions
        Flow-->>Kafka: charge или release
    end

    Flow-->>Kafka: vk-outgoing-messages
    Kafka-->>VK: Доставить финальный ответ
    VK-->>Kafka: vk-message-delivery-results
    Kafka-->>Flow: Complete или refund
Loading

Webhook, state transitions и обязательные исходящие команды сохраняются в БД. Долгие шаги проходят через Kafka. Повторная доставка компенсируется стабильными requestId, уникальными ключами, row locks, журналами side effects и проверками состояния.

Сервисы

Модуль Ответственность Порт
vk-connector-service VK webhook, credentials, профили, участники, delivery journal и retries 8081
orchestrator-service Saga/state machine, pricing validation, compensation, outbox, watchdog и DLT registry 8082
ai-service OpenRouter, generation journal и AI conversation projection 8083
tg-connector-service Telegram-боты, Mini App API/projections, billing, students и broadcasts 8084
shared-libs Общие pricing, Kafka correlation/retry и AES-256-GCM библиотека
mini-app React/Vinext Telegram Mini App и Cloudflare Worker artifact отдельно

Каждый прикладной сервис владеет своей PostgreSQL database и Flyway миграциями. Межсервисного чтения таблиц нет; HTTP используется только на внешних границах, а внутренние команды и события передаются через Kafka.

Критические инварианты

  • AI-текст не отправляется без curator decision.
  • Ручной ответ не вызывает OpenRouter и не списывает AI-кредиты.
  • Повтор события не должен повторять резерв, charge, refund, AI-вызов или отправку сообщения.
  • Стоимость вычисляется из provider usage.cost общей формулой max(minimum_charge, ceil(provider_cost_usd * credits_per_usd)).
  • Orchestrator и TG billing независимо проверяют pricing и итоговую сумму.
  • Необратимая ошибка оплаченной доставки запускает идемпотентный refund.
  • Секреты VK в БД шифруются AES-256-GCM; plaintext credentials не должны попадать в Git или логи.
  • Mini App доверяет только Telegram initData в заголовке X-Telegram-Init-Data, проверенному backend по HMAC, возрасту и curator ownership.

Технологии

Область Текущее состояние
Backend Java 21, Spring Boot 3.5.15, Gradle wrapper 8.7
Messaging Apache Kafka, Spring Kafka
Persistence PostgreSQL, Spring Data JPA, Hibernate, Flyway
Cache/rate limit Redis, Lettuce, Bucket4j, Caffeine
Mini App TypeScript 5.9.3, React 19.2.6, Vinext 0.0.50, Vite 8.0.13, Tailwind CSS 4.2.1
Mini App runtime Node.js 22.13+, pnpm 11.9.0, Cloudflare Vite plugin/Worker
Observability Actuator, Micrometer, Prometheus, Grafana, Alertmanager
Tests JUnit 5, Mockito, AssertJ, Spring Boot Test, Testcontainers, Node test runner, ESLint

Application default OpenRouter model — openai/gpt-5.5; production model задаётся явно через обязательную переменную OPENROUTER_MODEL. Production preflight и Compose отклоняют пустое значение; .env.prod.example использует тот же openai/gpt-5.5.

Структура репозитория

.
|-- shared-libs/                 Общие backend-компоненты
|-- vk-connector-service/        VK intake, directory sync и delivery
|-- orchestrator-service/        Workflow state machine и compensation
|-- ai-service/                  OpenRouter и generation journal
|-- tg-connector-service/        Telegram, Mini App API, billing, broadcasts
|-- mini-app/                    React/Vinext Mini App
|-- docs/                        Runbook, checklist и профильные контракты
|-- infra/                       PostgreSQL init, Caddy, monitoring, stubs
|-- load-tests/                  k6 VK webhook scenario
|-- scripts/                     Preflight, staging, load, backup/restore
|-- .github/workflows/           CI и ручной staging load workflow
|-- docker-compose.yml           Только локальная инфраструктура
|-- compose.staging.yml          Полный изолированный E2E-стенд
`-- compose.prod.yml             Single-host backend/infra profile

Локальный запуск

Нужны JDK 21, Docker Engine с Compose v2, Node.js 22.13+ и pnpm 11.9.0.

Создайте локальный env-файл и заполните credentials:

cp .env.example .env

Поднимите PostgreSQL, Redis, Kafka и Kafka UI:

docker compose up -d

docker-compose.yml не содержит приложений. Запустите сервисы в отдельных терминалах из корня репозитория:

./gradlew :vk-connector-service:bootRun
./gradlew :orchestrator-service:bootRun
./gradlew :ai-service:bootRun
./gradlew :tg-connector-service:bootRun

Затем запустите Mini App:

pnpm --dir mini-app install --frozen-lockfile
pnpm --dir mini-app dev

Локальный Vite server проксирует /api/miniapp/** в 127.0.0.1:8084. Live-режим требует Telegram initData; для изолированного UI-preview нужен явный NEXT_PUBLIC_DEMO_MODE=true.

Проверки

Backend unit tests:

./gradlew test --no-daemon

Integration tests с PostgreSQL/Kafka Testcontainers:

./gradlew integrationTest --no-daemon

Mini App:

pnpm --dir mini-app run lint
pnpm --dir mini-app run build
pnpm --dir mini-app run test

Конфигурация и security checks:

docker compose --env-file .env.prod.example -f compose.prod.yml config --quiet
sh scripts/verify-prod-openrouter-config.sh
docker compose -f compose.staging.yml config --quiet
sh scripts/secret-scan.sh

CI выполняет Java unit/integration tests, Mini App lint/build/tests, Compose и monitoring validation, ShellCheck, backup/restore drill и secret scan.

Окружения

  • docker-compose.yml — только локальные PostgreSQL 15, Redis 7, Kafka и Kafka UI; приложения запускаются отдельно.
  • compose.staging.yml — четыре приложения, PostgreSQL 17, Redis, Kafka и WireMock. Провайдеры изолированы, TG polling выключен, auto-curator включён только с confirmation values.
  • compose.prod.yml — четыре backend-приложения, PostgreSQL 17, Redis AOF, single-node Kafka, Caddy, Prometheus, Alertmanager и Grafana. Caddy публикует /vk/webhook и защищённый Telegram initData маршрут /api/miniapp/**; ops UI слушают localhost.
  • mini-app/.openai/hosting.json — отдельный Sites/Cloudflare deployment frontend. Worker проксирует same-origin API-запросы на origin из runtime variable MINI_APP_API_ORIGIN.

Production containers приложений запускаются не от root, с read-only filesystem, no-new-privileges, health checks и ротацией логов. Production профиль не настраивает PostgreSQL failover, multi-node Kafka или несколько экземпляров приложений.

Текущие ограничения

  • Active broadcast после reload восстанавливается через API только на стадии выбора получателей; AWAITING_TEXT и READY нельзя продолжить из UI.
  • AI использует один OpenRouter API key; provider failover и ротации нет.
  • Conversation summaries, retention и удаление персональных данных не реализованы.
  • Broadcast scheduling, сегментация и AI-генерация текста не реализованы.
  • Для Google Таблиц создано owner-scoped хранилище snapshot/settings, но оно ещё не подключено к HTTP refresh/fallback; запись ячеек и переход к рассылке также не реализованы.

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

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages