🌐 Live: pdf.supabots.ru
Загружаешь PDF — презентацию, коммерческое предложение или лендинг — получаешь жёсткий аудит от ИИ по трём осям: дизайн, копирайтинг, продающая психология. Ответ честный, с конкретными цитатами из документа, готовыми улучшениями текста и приоритизированным планом действий.
-
🎯 Многогранный анализ. Модель видит страницы PDF как изображения, оценивает общую эффективность 0–100 и разбирает:
- визуальную иерархию, слепые зоны, читаемость;
- копирайтинг — слабые заголовки, клише, отсутствие выгод;
- продающую психологию — барьеры клиента, недостающие приёмы убеждения;
- конкретные переписывания текста с обоснованием;
- план действий с приоритетами High / Medium / Low.
-
🔀 Гибридная цепочка моделей. Сначала пробуем бесплатные vision-модели OpenRouter — если все четыре упали (rate-limit, timeout, schema fail), включается платный
google/gemini-3-flash-preview, который принимает PDF нативно одним файлом (~$0.01–0.05 за анализ). В 90% случаев хватает бесплатных. -
📺 Живой прогресс через SSE. Вместо тупого спиннера — «Готовим страницу 4 из 10», «Анализируем — модель 2 из 5 (Gemma 4 31B)», «Модель не ответила, переходим к следующей». Никаких «крутится и непонятно».
-
🔒 Секреты только на сервере. Фронт не знает про OpenRouter в принципе — ни в бандле, ни в DevTools. Бэкенд слушает исключительно на
127.0.0.1, наружу торчит через reverse-proxy того же домена. -
📄 Умный лимит страниц. Для документов >10 страниц показывается предупреждение перед анализом и пометка «Проанализировано 10 из 23 страниц» в шапке отчёта. Никаких «тихих» обрезаний.
-
💾 История на клиенте. Все прошлые отчёты хранятся в
localStorageбраузера. Бэкенд stateless — никаких файлов на диске, никакой БД.
flowchart LR
Browser["🌐 Браузер<br/>React 19 + Vite<br/>SPA"]
Nginx["Nginx<br/>reverse-proxy + SSL"]
Node["Node.js Backend<br/>Express + SSE"]
OR["OpenRouter<br/>vision + file models"]
Browser -- "POST /api/analyze<br/>multipart PDF" --> Nginx
Nginx --> Node
Node -- "HTTPS<br/>fallback chain" --> OR
OR -- "JSON" --> Node
Node -- "SSE stream<br/>stage · render_progress<br/>attempt · result · error" --> Browser
Поток:
- Фронт отдаёт PDF multipart'ом на
POST /api/analyze. - Бэкенд проверяет magic-байты
%PDF-, парсит черезpdfjs-dist+@napi-rs/canvas(без системных зависимостей). - Для каждой модели в цепочке: рендерит страницы в JPEG (для vision-моделей) или кодирует PDF в base64 (для file-capable платных), вызывает OpenRouter с
response_format: json_object, валидирует ответ через zod. - Между попытками — SSE события
attempt/attempt_failed. Успех → событиеresultс финальнымAuditResult, включая метаданные (имя модели, сколько страниц проанализировано). - Фронт стримит события через
fetch().body.getReader()+ ручной SSE-парсер, обновляетProgressViewв реальном времени.
| Слой | Технологии |
|---|---|
| Фронт | React 19, Vite 6, TypeScript 5.8, Tailwind CSS, Recharts, Lucide, pdfjs-dist (lazy) |
| Бэк | Node.js 20+, Express 4, Multer, pdfjs-dist (legacy ESM), @napi-rs/canvas, zod, dotenv |
| LLM | OpenRouter: nvidia/nemotron-nano-12b-v2-vl:free, google/gemma-4-*:free, google/gemma-3-27b-it:free, google/gemini-3-flash-preview (paid fallback) |
| Тесты | Vitest (32 unit-тестa) |
| Деплой | systemd unit, nginx reverse-proxy, Let's Encrypt |
Требуется Node.js 20+ и npm. Понадобится API-ключ OpenRouter — получить бесплатно на openrouter.ai/keys.
# 1. Зависимости фронта
npm ci
# 2. Зависимости и .env бэкенда
cd server
npm ci
cp .env.example .env
# открой server/.env и впиши реальный OPENROUTER_API_KEY
# 3. Запустить бэкенд (один терминал)
npm run dev
# слушает http://127.0.0.1:3217
# 4. Запустить фронт (другой терминал, из корня репо)
cd ..
npm run dev
# открывается на http://localhost:3000 с dev-прокси /api → 3217Открывай http://localhost:3000 и загружай PDF.
cd server && npm testОжидается 32+ passing (parseJsonLoose, zod-схемы, цепочка фолбеков, классификация ошибок, рендер PDF, file/image режимы).
.
├── App.tsx # монолитный корень фронта
├── components/
│ ├── FileUpload.tsx # дропзона + подсказка о 10 страницах
│ ├── ProgressView.tsx # живой SSE-прогресс
│ ├── ReportCard.tsx # карточка одной секции отчёта
│ ├── SaaSSections.tsx # блок «об авторе»
│ └── ...
├── services/
│ ├── analyzeService.ts # POST /api/analyze + SSE-парсер
│ ├── historyService.ts # localStorage история
│ └── pdfService.ts # экспорт отчёта в PDF (html2canvas + jsPDF)
├── server/
│ ├── src/
│ │ ├── index.ts # Express bootstrap
│ │ ├── config.ts # ENV loader
│ │ ├── routes/analyze.ts # POST /api/analyze + /api/health
│ │ └── lib/
│ │ ├── openrouter.ts # цепочка моделей, image/file режимы
│ │ ├── pdfRender.ts # PDF → JPEG через pdfjs-dist
│ │ ├── prompts.ts # system + user промпты
│ │ ├── sse.ts # Server-Sent Events helper
│ │ └── validate.ts # zod-схемы + parseJsonLoose
│ └── .env.example # шаблон без секретов
├── scripts/
│ ├── deploy.sh # tar-pipe деплой через SSH
│ ├── deploy.env.example # шаблон
│ └── auditai.service.template # systemd unit
└── docs/superpowers/
├── specs/ # дизайн-спецификация миграции
└── plans/ # пошаговый план реализации
- Скопируй
scripts/deploy.env.example→scripts/deploy.env(в.gitignore), заполниVPS_HOST,VPS_USER,VPS_REMOTE_PATH. - На VPS положи
.envс настоящимOPENROUTER_API_KEYв$VPS_REMOTE_PATH/server/.env(chmod 600, вне веб-корня). - Поставь systemd unit из
scripts/auditai.service.template. - Настрой nginx vhost — пример location-блока с
proxy_buffering offиproxy_read_timeout 360sсм. в спецификации. ./scripts/deploy.sh— собирает, заливает, рестартит сервис, делает health-check.
Минимальные требования VPS: Ubuntu 22.04+ / Debian 12, 2 GB RAM, Node 20+, nginx, certbot.
- Никаких секретов в git-истории и во фронт-бандле. Проверяется через
grep -r "sk-or" dist/на каждый билд. - Backend слушает только
127.0.0.1:PORT. - Upload лимит 20 MB, валидация magic-байтов
%PDF-до парсинга. - Санитизация пользовательского контекста (audience / goal) перед интерполяцией в промпт.
- CORS не нужен — фронт и бэк на одном origin через nginx.
MIT — делай что хочешь, но на свой страх и риск.
Upload a PDF — a pitch deck, sales proposal or landing page — and get a brutal AI audit across three axes: design, copywriting, sales psychology. Honest answers, specific quotes from your document, ready-to-use rewrites and a prioritised action plan.
-
🎯 Multi-angle analysis. The model sees PDF pages as images, scores overall effectiveness 0–100 and dissects:
- visual hierarchy, blind spots, readability;
- copywriting — weak headlines, clichés, missing benefits;
- sales psychology — customer barriers, missing persuasion techniques;
- concrete text rewrites with reasoning;
- action plan with High / Medium / Low priorities.
-
🔀 Hybrid fallback chain. First tries free OpenRouter vision models — if all four fail (rate-limit, timeout, schema mismatch), activates paid
google/gemini-3-flash-previewwhich accepts the PDF natively as a single file (~$0.01–0.05 per analysis). Free tier is enough in ~90% of cases. -
📺 Live SSE progress. No silent spinner — real status: "Rendering page 4 of 10", "Analysing — model 2 of 5 (Gemma 4 31B)", "Model didn't respond, moving on". You always know what's happening.
-
🔒 Secrets on the server only. Frontend has zero knowledge of OpenRouter — nothing in the bundle, nothing in DevTools. Backend listens only on
127.0.0.1, exposed externally only via reverse-proxy on the same domain. -
📄 Smart page cap. For documents longer than 10 pages, a warning pops up before analysis and a "10 of 23 pages analysed" badge appears in the report header. No silent truncation.
-
💾 Client-side history. All past reports live in browser
localStorage. The backend is stateless — no files on disk, no database.
flowchart LR
Browser["🌐 Browser<br/>React 19 + Vite<br/>SPA"]
Nginx["Nginx<br/>reverse-proxy + SSL"]
Node["Node.js Backend<br/>Express + SSE"]
OR["OpenRouter<br/>vision + file models"]
Browser -- "POST /api/analyze<br/>multipart PDF" --> Nginx
Nginx --> Node
Node -- "HTTPS<br/>fallback chain" --> OR
OR -- "JSON" --> Node
Node -- "SSE stream<br/>stage · render_progress<br/>attempt · result · error" --> Browser
Flow:
- Frontend sends the PDF as multipart to
POST /api/analyze. - Backend verifies
%PDF-magic bytes, parses viapdfjs-dist+@napi-rs/canvas(no system deps). - For each model in the chain: renders pages to JPEG (for vision models) or base64-encodes the whole PDF (for file-capable paid models), calls OpenRouter with
response_format: json_object, validates response with zod. - Between attempts — SSE events
attempt/attempt_failed. Success →resultevent with finalAuditResultincluding metadata (which model served, how many pages analysed). - Frontend streams events via
fetch().body.getReader()+ hand-rolled SSE parser, updatesProgressViewin real time.
| Layer | Tech |
|---|---|
| Frontend | React 19, Vite 6, TypeScript 5.8, Tailwind CSS, Recharts, Lucide, pdfjs-dist (lazy) |
| Backend | Node.js 20+, Express 4, Multer, pdfjs-dist (legacy ESM), @napi-rs/canvas, zod, dotenv |
| LLM | OpenRouter: nvidia/nemotron-nano-12b-v2-vl:free, google/gemma-4-*:free, google/gemma-3-27b-it:free, google/gemini-3-flash-preview (paid fallback) |
| Tests | Vitest (32 unit tests) |
| Deploy | systemd unit, nginx reverse-proxy, Let's Encrypt |
Requires Node.js 20+ and npm. You'll need an OpenRouter API key — free at openrouter.ai/keys.
# 1. Frontend deps
npm ci
# 2. Backend deps and .env
cd server
npm ci
cp .env.example .env
# open server/.env and paste your OPENROUTER_API_KEY
# 3. Run backend (terminal 1)
npm run dev
# listens on http://127.0.0.1:3217
# 4. Run frontend (terminal 2, from repo root)
cd ..
npm run dev
# opens at http://localhost:3000 with dev proxy /api → 3217Open http://localhost:3000 and upload a PDF.
cd server && npm testExpect 32+ passing (parseJsonLoose, zod schemas, fallback chain, error classification, PDF rendering, file/image modes).
.
├── App.tsx # monolithic frontend root
├── components/
│ ├── FileUpload.tsx # dropzone + 10-page hint
│ ├── ProgressView.tsx # live SSE progress
│ ├── ReportCard.tsx # single report section card
│ ├── SaaSSections.tsx # author bio block
│ └── ...
├── services/
│ ├── analyzeService.ts # POST /api/analyze + SSE parser
│ ├── historyService.ts # localStorage history
│ └── pdfService.ts # report export to PDF (html2canvas + jsPDF)
├── server/
│ ├── src/
│ │ ├── index.ts # Express bootstrap
│ │ ├── config.ts # ENV loader
│ │ ├── routes/analyze.ts # POST /api/analyze + /api/health
│ │ └── lib/
│ │ ├── openrouter.ts # model chain, image/file modes
│ │ ├── pdfRender.ts # PDF → JPEG via pdfjs-dist
│ │ ├── prompts.ts # system + user prompts
│ │ ├── sse.ts # Server-Sent Events helper
│ │ └── validate.ts # zod schemas + parseJsonLoose
│ └── .env.example # template without secrets
├── scripts/
│ ├── deploy.sh # tar-pipe deploy over SSH
│ ├── deploy.env.example # template
│ └── auditai.service.template # systemd unit
└── docs/superpowers/
├── specs/ # migration design spec
└── plans/ # step-by-step implementation plan
- Copy
scripts/deploy.env.example→scripts/deploy.env(gitignored), fill inVPS_HOST,VPS_USER,VPS_REMOTE_PATH. - On the VPS put
.envwith a realOPENROUTER_API_KEYat$VPS_REMOTE_PATH/server/.env(chmod 600, outside web root). - Install the systemd unit from
scripts/auditai.service.template. - Configure nginx vhost — location block with
proxy_buffering offandproxy_read_timeout 360s, see spec. ./scripts/deploy.sh— builds, uploads, restarts the service, runs health check.
Minimum VPS specs: Ubuntu 22.04+ / Debian 12, 2 GB RAM, Node 20+, nginx, certbot.
- Zero secrets in git history or frontend bundle. Verified with
grep -r "sk-or" dist/on every build. - Backend listens only on
127.0.0.1:PORT. - 20 MB upload limit,
%PDF-magic-byte validation before parsing. - User context (audience / goal) sanitised before prompt interpolation.
- No CORS needed — frontend and backend on the same origin via nginx.
MIT — do whatever you want, at your own risk.
Author: Konstantin · YouTube · Telegram