c1bfb5f40ec0ca5af00beaaffc884ee8fe2626cf
Redis:
- Singleton ConnectionPool (redis.asyncio), 50 connections — не создаём
новое TCP-соединение на каждый HTTP-запрос
Rate limiter:
- Полностью переписан на async/await
- Lua-скрипт _LUA_CHECK_AND_INCR — атомарная проверка+инкремент без race condition
- Lua-скрипт _LUA_ACQUIRE_CONCURRENT — атомарный захват слота задачи
- Старый паттерн INCR→check→DECR удалён (race condition при конкурентных запросах)
Security:
- get_current_user кэширует пользователя в Redis на 5 минут (TTL)
Раньше: SELECT users на каждый HTTP-запрос
Теперь: Redis GET (кэш) → SELECT users (только при промахе)
- hashed_password НЕ кладётся в кэш
- invalidate_user_cache() для сброса при смене тарифа/пароля
- get_ws_user() для WebSocket через ?token=JWT (браузеры не могут
передавать Authorization header при WS-handshake)
WebSocket:
- Добавлена аутентификация (Depends(get_ws_user))
- Проверка ownership задачи ДО accept() соединения
- Чужой task_id → закрытие с кодом 4004
URL obfuscation:
- Task.public_id = secrets.token_urlsafe(16) = 22 случайных base64url символа
- Клиент работает только с public_id, внутренний UUID не раскрывается
- Все роутеры переключены на public_id в WHERE условиях
- TaskResponse больше не возвращает input_data (там minio_key и т.д.)
- Миграция 002_add_task_public_id.py
MinIO:
- Singleton клиент (не создаём новый на каждый upload)
- ensure_bucket() вызывается один раз при старте (lifespan), не на каждый запрос
- Путь uploads/{doc_uuid}{ext} — user_id убран из пути
CORS:
- Убраны wildcard allow_methods/allow_headers (несовместимы с credentials=True)
- Явный список: methods=[GET,POST,DELETE,OPTIONS], headers=[Authorization,Content-Type,Accept]
- Swagger/OpenAPI доступны только в ENVIRONMENT=development
Documents:
- Content-Length проверяется ДО чтения тела (ранняя отбивка больших файлов)
- Повторная проверка реального размера после чтения (защита от поддельного заголовка)
- Используем get_current_verified_user вместо get_current_user (требуем подтверждённый email)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Академический помощник
Микросервисная система антиплагиата и поиска академических источников.
Студент вводит тему → система ищет источники → проверяет плагиат → форматирует ГОСТ-библиографию. Всё асинхронно: студент закрыл браузер, получил email когда готово.
Архитектура
┌─────────────────┐
│ Frontend │ React + Vite + TypeScript
│ (React SPA) │
└────────┬────────┘
│ HTTPS
┌────────▼────────┐
│ API Gateway │ FastAPI, порт 8000
│ (FastAPI) │ JWT, Rate Limit, WebSocket
└──┬─────────┬───┘
│ RabbitMQ│ Celery задачи
┌────────────▼──┐ ┌──▼──────────────┐
│ worker-gpu │ │ worker-indexer │
│ (CUDA/FAISS) │ │ (PDF/DOCX parse) │
│ Sem. search │ │ Winnowing/MinHash │
│ LLM paraphrase│ └──────────────────┘
└────────────────┘
│
┌─────────▼──────────┐ ┌────────────────────┐
│ worker-notifier │ │ worker-gost │
│ (SMTP email) │ │ (ГОСТ 7.1/7.0.5) │
└────────────────────┘ └────────────────────┘
Инфраструктура:
PostgreSQL 16 · Redis 7 · RabbitMQ 3 · Elasticsearch 8
MinIO (4TB) · Ollama (llama3:8b) · NVIDIA RTX 2060
Быстрый старт
# 1. Клонировать репозиторий
git clone https://github.com/jze9/anti-plagiarism.git
cd anti-plagiarism
# 2. Создать файл конфигурации
cp .env.example .env
# Отредактировать .env — сменить пароли и ключи!
# 3. Запустить в режиме разработки
make dev
# 4. Применить миграции базы данных
make migrate-dev
# 5. Создать ES индекс
make es-init
# 6. Открыть браузер
# Frontend: http://localhost:5173
# API docs: http://localhost:8000/api/docs
# RabbitMQ: http://localhost:15672 (guest/guest)
# Flower: http://localhost:5555
# MinIO: http://localhost:9001
Команды
make dev # Запуск в dev режиме (hot reload)
make build # Сборка Docker образов
make up # Запуск в продакшн режиме
make down # Остановить все сервисы
make migrate # Применить Alembic миграции
make logs # Логи всех сервисов
make shell-api # Shell в контейнере API
make shell-gpu # Shell в контейнере GPU воркера
make lint # Запустить линтер
make test # Запустить тесты
make clean # Удалить контейнеры и volumes
Стек технологий
| Компонент | Технологии |
|---|---|
| API Gateway | FastAPI 0.111, Python 3.11, SQLAlchemy 2.0, Alembic |
| GPU Worker | sentence-transformers, FAISS-GPU (IVFFlat), Ollama (llama3:8b) |
| Indexer | PyMuPDF, python-docx, Winnowing, MinHash LSH |
| Очереди | RabbitMQ (брокер) + Celery 5 (воркеры) + Redis (результаты) |
| База данных | PostgreSQL 16 |
| Поиск | Elasticsearch 8 (BM25) + FAISS GPU (cosine) |
| Хранилище | MinIO (S3-совместимый) |
| Frontend | React 18, Vite, TypeScript, TailwindCSS, Zustand, React Query v5 |
Проверка плагиата (4 уровня)
- Winnowing + MinHash (~мс) — точное совпадение по fingerprints
- n-граммы + Jaccard (~сек) — нечёткое совпадение
- FAISS GPU cosine (~мс) — семантическое сходство (порог 0.75)
- Ollama Llama3 (~2сек) — LLM анализ парафраза (confidence > 0.7)
Тарифные планы
| Тариф | Цена | Поиск/день | Изложений/мес | Плагиат/мес | Одновременно |
|---|---|---|---|---|---|
| Бесплатный | 0₽ | 10 | 3 | 1 | 1 |
| Студенческий | 199₽ | безлимит | 30 | 10 | 2 |
| Премиум | 499₽ | безлимит | безлимит | 50 | 5 |
| Научный | 999₽ | безлимит | безлимит | безлимит | 10 |
Парсеры источников
# Запарсить OpenAlex
python scripts/run_parser.py openalex \
--query "машинное обучение" \
--limit 10000 \
--output /data/processed
# КиберЛенинка
python scripts/run_parser.py cyberleninka \
--query "нейронные сети" \
--limit 1000
# arXiv
python scripts/run_parser.py arxiv \
--query "deep learning" \
--categories cs.AI cs.LG \
--limit 5000
Переменные окружения
Смотри .env.example для полного списка переменных.
Обязательно смените SECRET_KEY, POSTGRES_PASSWORD, MINIO_SECRET_KEY.
Продакшн деплой
# На сервере
cp .env.example .env
nano .env # Настроить все пароли и ключи
make build
make up
make migrate
# SSL сертификат (Let's Encrypt)
certbot --nginx -d academic.jze9.ru
Лицензия
MIT
Description
Languages
Python
63.4%
TypeScript
32.7%
Makefile
1.5%
Shell
1.3%
Dockerfile
0.7%
Other
0.3%