No description
Find a file
febux 942dc531a8
Some checks are pending
deploy / build-and-deploy (push) Waiting to run
ci / Lint & typecheck (push) Successful in 28s
ci / Unit tests (push) Successful in 1m3s
Update deploy.yml
2026-09-14 02:13:11 +03:00
.forgejo/workflows Update deploy.yml 2026-09-14 02:13:11 +03:00
.github/workflows Fix CI integration tests. 2026-09-06 20:51:16 +03:00
deploy Fix CA misconfig for Nomad. 2026-09-14 00:16:39 +03:00
docs Fix CI workflows. Extend make commands. Fix Nomad deploy doc. 2026-09-13 23:54:06 +03:00
migrations User profile with billing base was added. 2026-08-25 20:52:50 +03:00
src/contract_check Fix stream not read error for files whose recieved from bot. 2026-09-06 20:41:35 +03:00
srv Switch observability to passive collection: remove OTLP push, Vector replaces otel-collector 2026-09-06 19:17:08 +03:00
tests Fix CI integration tests. 2026-09-06 21:23:42 +03:00
.dockerignore Init commit 2026-08-12 21:29:36 +03:00
.env.bot.example M1: bot webhook delivery - secret-token aiohttp server, /healthz, mode switch, TLS-edge routing (#001, #002) 2026-09-06 14:53:22 +03:00
.env.example Fix Vector config and OpenObserve auth for live E2E verification 2026-09-06 19:41:04 +03:00
.gitignore User profile with billing base was added. 2026-08-25 20:52:50 +03:00
.pre-commit-config.yaml Architecture diagram was created. 2026-09-12 13:00:57 +03:00
.python-version build: upgrade to Python 3.14 on Debian Trixie 2026-09-06 15:29:49 +03:00
alembic.ini Phase 2 - Raw SQL migration, pytest-cov coverage gate, CI branch 2026-08-24 00:45:39 +03:00
CONTEXT.md Plan for ADR and payments. 2026-08-25 21:57:05 +03:00
docker-compose.bot.yml M1: bot webhook delivery - secret-token aiohttp server, /healthz, mode switch, TLS-edge routing (#001, #002) 2026-09-06 14:53:22 +03:00
docker-compose.test.yml Fix CI integration tests. 2026-09-06 20:51:16 +03:00
docker-compose.yml Fix Vector config and OpenObserve auth for live E2E verification 2026-09-06 19:41:04 +03:00
Makefile Fix CI workflows. Extend make commands. Fix Nomad deploy doc. 2026-09-13 23:54:06 +03:00
pyproject.toml Switch observability to passive collection: remove OTLP push, Vector replaces otel-collector 2026-09-06 19:17:08 +03:00
README.md Git check 2026-09-13 04:31:10 +03:00
uv.lock Switch observability to passive collection: remove OTLP push, Vector replaces otel-collector 2026-09-06 19:17:08 +03:00

Контракт-чек

LLM-сервис скрининга рисков в договорах по ГК РФ / ГК РБ. Telegram-бот + web-аутентификация (email/password, JWT-пара; пасскеи и магические ссылки — passwordless) + B2B API + биллинг (ЮKassa: подписки, пакеты кредитов, авто-возвраты) сейчас; web-SPA — позже.

Pipeline: PDF/DOCX/RTF/TXT/CSV/изображения (pymupdf / mammoth / tesseract OCR) → текст → чанки → прескрин (гибридная эвристика + LLM: метаданные договора, роутинг) → LLM-анализ (Ollama Cloud или YandexGPT, json-schema + repair-loop) → markdown-отчёт с цитатами, ссылкой на пункт и дисклеймером «не заменяет юриста».

Документы

  • docs/ARCHITECTURE.mdединственный источник истины для production-рефакторинга. Hexagonal-архитектура, RabbitMQ-конвейер, схемы БД, очередь/ретраи/DLQ, конфиг, deploy, observability.
  • docs/DEPLOY.md — руководство по развёртыванию: локально, Docker Compose, VPS, seed-token, backup, troubleshooting.
  • docs/BUSINESS_IDEA.md — идея и бизнес-модель.
  • docs/TICKETS.md — тикеты реализации со статусами.

Архитектура (одна строка)

Hexagonal (ports & adapters). Ядро contract_check.core владеет всем состоянием (Postgres/MinIO/RabbitMQ/LLM/кредиты/биллинг). Семь сервисов собираются из него:

Telegram ──► bot (aiogram, HTTP-only) ──HTTP──► api (FastAPI) ──publish──► RabbitMQ
                                                   владеет: PG, MinIO,        │
                                                   Redis, кредитами            ▼
                                                                          extract.q ──► worker-extract
                                                                       (pymupdf/mammoth/tesseract, CPU) │
                                                                                                       ▼ publish
                                                                          prescreen.q ──► worker-prescreen
                                                                  (гибридные метаданные + роутинг) │
                                                            ┌──────────────────────────────────────┤
                                                            ▼ deep_analysis                manual_review
                                                      analyze.q ──► worker-analyze               │
                                                          (LLM, I/O) → Report                      │
                                                                                                    ▼
                                                              notify.q ──► worker-notify (SMTP: password reset, magic link и др.)
  • api — единственный писатель для пользовательских мутаций (upload → reserve credit → MinIO → publish DocumentUploaded).
  • worker-extract — CPU: достаёт текст (PDF/DOCX/RTF/TXT/CSV, OCR для изображений; детект формата через python-magic), грузит извлечённый текст в MinIO, публикует PrescreenRequested (если PRESCREEN_ENABLED=true) или сразу DocumentExtracted в analyze.q.
  • worker-prescreen — гибридное извлечение метаданных договора (Stage 1 эвристика + Stage 2 LLM при низкой уверенности), роутинг: deep_analysis → публикует AnalyzeRequested в analyze.q; manual_review — терминальный статус; auto_approve — записывает лёгкий Report, status=done. Пишет в prescreen_results.
  • worker-analyze — I/O: LLM-анализ по чек-листу, валидация/repair, сохраняет Report, status=done.
  • worker-notify — доставка email-уведомлений (восстановление пароля и др.) через SMTP; без SMTP_HOST — dev-логгер.
  • worker-billing — таймерный планировщик биллинга (без MQ): продления подписок (счета за 3 дня до конца периода), expiry/past_due-переходы, сверка pending-счетов старше 15 минут через ЮKassa API.
  • bot — адаптер: HTTP-клиент к api, не импортирует core.db/s3/llm/mq (граница проверяется тестом tests/unit/test_bot_boundary.py). Может работать на отдельном сервере — см. docs/DEPLOY.md §14.

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

src/contract_check/
    core/                        # общий домен (импортируется каждым сервисом)
      config.py  logging.py  telemetry.py  sentry.py  metrics.py  errors.py
      credits.py  tokens.py  api_keys.py  rate_limit.py  redis_client.py
      auth.py  auth_refresh.py  auth_refresh_key.py  passkeys.py
      db/      models.py  session.py  enums.py  repositories/
      mq/      topology.py  publisher.py  consumer.py  messages.py
      s3/      port.py  minio_storage.py
      llm/     port.py  factory.py  ollama_cloud.py  yandex_gpt.py  prescreen.py  errors.py
      billing/     port.py  yookassa.py  errors.py  quota.py  fulfillment.py  refunds.py
      extraction/   port.py  factory.py  formats.py  + adapters/
                    (pdf_pymupdf, docx_mammoth, rtf_striprtf, txt_chardet, ocr_tesseract)
      analysis/     extractor.py  chunker.py  checklist.py  report_schema.py  ocr.py  analyzer.py
      notifications/  transport.py  publisher.py      # SMTP + dev-log
      security/     passwords.py                      # argon2
  api/                         # FastAPI-образ
    app.py  deps.py  middleware.py  services.py  __main__.py
    admin/                       # серверный UI по /admin (login, users, счета, возвраты, hold)
      billing.py  users.py  auth.py  router.py  templating.py  templates/
    billing/                     # token-gated HTML pay-страницы (/pay/{id})
    routes/                      # префикс /api/v1
      __init__.py  health.py  documents.py  reports.py  me.py  metrics.py  b2b.py  billing.py  webhooks.py
      auth/                      # Telegram, email/password, passkeys, magic links
        __init__.py  telegram.py  password.py  passkeys.py  magic_links.py  support.py
    schemas/                     # Pydantic-схемы запросов/ответов
      auth.py  b2b.py  billing.py  common.py  documents.py  me.py  profile.py  reports.py
  worker_extract/              # CPU-образ (pymupdf + mammoth + tesseract)
    consumer.py  handler.py  extract_document.py  __main__.py
  worker_prescreen/            # прескрин-образ (гибрид: эвристика + LLM)
    consumer.py  handler.py  router.py  config.py
    extractor.py  extractor_heuristic.py  extractor_llm.py  extractor_hybrid.py
  worker_analyze/              # I/O-образ (LLM provider)
    consumer.py  handler.py  __main__.py
  worker_notify/               # email-уведомления (aiosmtplib)
    consumer.py  handler.py  __main__.py
  worker_billing/              # планировщик биллинга (продления, expiry, сверка)
    scheduler.py  __main__.py
  bot/                         # aiogram-адаптер (самый «тощий» образ: только core.logging)
    client.py  config.py  handlers.py  rate_limit.py  __main__.py
docs/                          # документация проекта (README остаётся в корне)
srv/                           # Dockerfile-ы (один на сервис, deps заточены)
  api/  worker-extract/  worker-prescreen/  worker-analyze/  worker-notify/  worker-billing/  bot/
migrations/                    # alembic (async): 0001_initial … 0011_user_profiles_billing
tests/
  conftest.py
  unit/        chunker, extractor, extraction_factory/adapters, llm_ollama_cloud,
               llm_yandex_gpt, llm_prescreen_extraction, prescreen_extractor(+heuristic/
               hybrid/llm), prescreen_router, credits, messages, rate_limit,
               checklist_report, auth, web_auth, passkeys, extract_handler,
               bot_client, bot_boundary, refunds, worker_billing
  integration/ upload_pipeline, extract_worker, prescreen_worker, analyze_worker,
               b2b_api, credits_db, auth_flow, passkeys_magic_link, admin_panel,
               profile_api, b2b_profile, billing_checkout, webhook_yookassa,
               pay_page, subscription_purchase, quota_reservation, refunds,
               admin_billing
Makefile                       # повседневные команды (make help)
docker-compose.yml             # default = инфра; --profile services = стек; --profile edge = nginx+certbot
pyproject.toml                 # hatchling + PEP 735 dependency-groups (db/mq/s3/obs/api/extract/prescreen/analyze/notify/billing/bot/dev)
.env.example                   # полный список env (см. docs/ARCHITECTURE.md §11)

Быстрый старт

Локально (разработка)

uv sync --group dev                              # все группы для локальной разработки
cp .env.example .env                             # впишите OLLAMA_* / YANDEXGPT_* / JWT_SECRET / BOT_TOKEN
docker compose up -d                             # только инфра (postgres/redis/rabbitmq/minio)
uv run alembic upgrade head                      # миграции
uv run python -m contract_check.api              # api на :8000
uv run python -m contract_check.worker_extract   # воркер экстракции
uv run python -m contract_check.worker_prescreen # воркер прескрина
uv run python -m contract_check.worker_analyze   # воркер анализа
uv run python -m contract_check.worker_notify    # воркер уведомлений
uv run python -m contract_check.worker_billing   # планировщик биллинга
uv run python -m contract_check.bot              # Telegram-бот

Через Docker Compose / Make

cp .env.example .env                             # заполнить секреты (DB, Rabbit, MinIO, LLM, BOT_TOKEN, JWT_SECRET, ...)
docker compose up -d                             # только инфра с healthchecks
docker compose --profile services up -d --build  # + api, worker-extract, worker-prescreen, worker-analyze, worker-notify, worker-billing (без бота)
docker compose --profile bot up -d --build       # + bot (можно запускать отдельно или на другом хосте)

Или через make: make dev (install + infra + migrate + api + workers + bot), make help — полный список целей (lint, typecheck, test, seed-token, jwt-token, admin-promote, логи/шеллы сервисов и т.п.).

Профили:

  • services — api + worker-ы (без бота).
  • bot — Telegram-бот; можно поднять на этом же хосте или на отдельном сервере (docs/DEPLOY.md §14).
  • edge — Nginx + certbot (deploy/nginx/, docs/DEPLOY.md §13).
  • obs / observer — observability (Grafana/Loki/Prometheus или OpenObserve + Vector).

Порты на хосте (смещены, чтобы не конфликтовать): Postgres 15432, Redis 17379, RabbitMQ AMQP 5672 / UI 15672, MinIO 9000 / console 9001, api 8000 / metrics 9100 (по умолчанию только на loopback), OpenObserve UI 5080, edge 80/443. Метрики worker-ов больше не публикуются на хост; их скрейпит Vector внутри сети compose.

API (кратко)

JSON-роуты под /api/v1; серверный admin UI — по /admin/* (FastAPI + Jinja2 + HTMX). Auth зависит от роута:

  • Пользовательские роуты (bot / web / Mini App) — Authorization: Bearer <user_jwt>. Telegram-JWT выдаётся через /api/v1/auth/telegram/*; web — JWT-пара (access + refresh, refresh отзывается через Redis); passwordless (пасскей / магическая ссылка) — одиночный access JWT.
  • Адаптер-level (только /api/v1/auth/telegram/bot) — Authorization: Bearer <service_token>.
  • B2BX-API-Key. Управление B2B-ключами требует пользовательский JWT.
  • Health/metrics — без auth.
  • /admin — HttpOnly cookie с user JWT + users.role = admin (или ADMIN_REQUIRED_ROLE).
Метод Путь Auth Назначение
GET /healthz, /readyz, /metrics liveness / readiness / Prometheus
POST /api/v1/auth/telegram/bot service token бот меняет verified telegram_id на JWT
POST /api/v1/auth/telegram/web Telegram Login Widget → JWT
POST /api/v1/auth/telegram/miniapp Mini App initData → JWT
POST /api/v1/auth/register регистрация email/password → JWT-пара
POST /api/v1/auth/login вход email/password → JWT-пара
POST /api/v1/auth/logout отзыв refresh-токена
POST /api/v1/auth/forgot-password reset-токен (хранится хэшем) + уведомление через notify.q
POST /api/v1/auth/reset-password задать новый пароль, отозвать все refresh
POST /api/v1/auth/passkeys/register/start, .../finish user JWT регистрация пасскея (WebAuthn, challenge в Redis)
POST /api/v1/auth/passkeys/authenticate/start, .../finish вход по пасскею (discoverable) → access JWT
GET/DEL /api/v1/auth/passkeys, /api/v1/auth/passkeys/{id} user JWT список / удаление пасскеев
POST /api/v1/auth/magic-link/request одноразовая ссылка входа на email (через notify.q)
POST /api/v1/auth/magic-link/verify обмен токена из письма на access JWT
GET /api/v1/auth/me, /api/v1/auth/me/permissions user JWT introspect JWT / права
POST /api/v1/documents user JWT multipart upload → reserve credit → MinIO → publish → 202 {document_id, correlation_id}
GET /api/v1/reports/{document_id} user JWT поллинг: {status, stage} или готовый 200 {markdown, findings, ...}
GET /api/v1/me user JWT профиль + credits_left
GET/PATCH /api/v1/me/profile user JWT пассивные настройки профиля (язык, TZ, уведомления, дашборд)
GET /api/v1/me/overview user JWT дашборд: документы, кредиты, план/квота, billing_hold
GET /api/v1/me/documents user JWT список документов пользователя
POST /api/v1/me/telegram, /api/v1/me/password user JWT привязка Telegram / смена пароля
GET /api/v1/billing/plans каталог тарифов + цена за документ
POST /api/v1/billing/checkout user JWT топ-ап кредитов или покупка подписки → 201 {invoice_id, confirmation_url}
GET /api/v1/billing/invoices, /api/v1/billing/invoices/{id} user JWT история / свой счёт
GET /api/v1/billing/subscription user JWT текущая подписка (active/past_due)
POST /api/v1/billing/subscriptions/autorenew user JWT вкл/выкл автопродление (по умолчанию OFF)
POST /api/v1/billing/refund user JWT авто-возврат по правилу D10; 409 если не возвращаем
POST /api/v1/webhooks/yookassa Basic (shopId:secret) уведомления ЮKassa; платёж перезапрашивается через REST
GET /pay/{invoice_id}?token=… подписанный JWT HTML-страница статуса счёта после оплаты
GET/PUT /api/v1/b2b/profile X-API-Key настройки профиля партнёра (без биллинга)
POST /api/v1/analyze X-API-Key B2B: анализ документа
GET /api/v1/b2b/reports/{id}, /api/v1/b2b/usage X-API-Key B2B: отчёт / usage
POST/GET /api/v1/b2b/keys, /api/v1/b2b/keys/{id}/revoke, .../usage user JWT управление B2B-ключами
GET/POST /admin/* admin cookie login/logout + пользователи (list, create, edit, ban, role, credits, verify-telegram), счета (/admin/invoices), возвраты, снятие billing-hold, gift-подписки

Биллинг-флаги: PLANS_ENABLED (квоты подписок; false = только кредиты), YOOKASSA_ENABLED (платежи; false → мутации биллинга 503, каталог читается). Детали: docs/ARCHITECTURE.md §8a, настройка ЮKassa — docs/DEPLOY.md §4a.

Полная спецификация роутов (схемы запросов/ответов, коды ошибок) — src/contract_check/api/routes/README.md; высокоуровневая поверхность — docs/ARCHITECTURE.md §15 (актуализируется).

Проверки (DoD)

uv run ruff check src tests && uv run ruff format --check src tests   # lint (или make lint)
uv run ty check src                                                   # типы (или make typecheck)
uv run pytest -m "not integration" -q                                 # unit, быстро
uv run pytest -m integration -q                                       # интеграционные (нужны контейнеры)