WA Dev Tools - набор веб-инструментов для разработчиков
Go to file
treamz 782149e3fa PDF: undo delete pages (30 sec window)
- Undo button appears after deleting pages
- Restores original file with all pages
- Re-renders all grids with restored pages
- Auto-hides after 30 seconds
2026-03-22 10:32:56 +03:00
.adminjs Dynamic content: landing and dashboard load texts from DB via API 2026-03-21 23:53:37 +03:00
lib Fix: about section (remove duplicate stat, fix text), placeholder public 2026-03-22 00:56:25 +03:00
public PDF: undo delete pages (30 sec window) 2026-03-22 10:32:56 +03:00
routes PDF preview: thumbnails via Ghostscript, preview in file cards 2026-03-22 01:22:25 +03:00
.gitignore Initial commit: WA Dev Tools service 2026-03-21 22:06:15 +03:00
ecosystem.config.js Refactor: modular architecture, security, config 2026-03-21 22:18:54 +03:00
EDITOR-DOC.md Initial commit: WA Dev Tools service 2026-03-21 22:06:15 +03:00
package-lock.json Add PDF tools: merge, split, rotate, delete, watermark, compress, convert, protect 2026-03-22 01:10:37 +03:00
package.json Add PDF tools: merge, split, rotate, delete, watermark, compress, convert, protect 2026-03-22 01:10:37 +03:00
README.md Add project documentation (README.md) 2026-03-22 01:19:23 +03:00
server.js Add PDF tools: merge, split, rotate, delete, watermark, compress, convert, protect 2026-03-22 01:10:37 +03:00
SVGEDITOR-FEATURES.md Initial commit: WA Dev Tools service 2026-03-21 22:06:15 +03:00

WA Dev Tools

Веб-платформа с набором инструментов для разработчиков. Включает 15 утилит для работы с изображениями, видео, PDF, кодом и веб-ресурсами — всё в одном защищённом интерфейсе с авторизацией.

Продакшн: wadevelop.ru / images.wadevelop.ru


Содержание

  1. Быстрый старт
  2. Архитектура
  3. Инструменты
  4. Авторизация
  5. Админ-панель
  6. API Reference
  7. Безопасность
  8. Конфигурация
  9. Деплой
  10. Мониторинг
  11. Технологии
  12. База данных

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

Требования

  • Node.js 18+
  • MariaDB 10.6+
  • FFmpeg (для видеоконвертера)
  • Ghostscript (для сжатия и конвертации PDF)

Установка

# Клонировать репозиторий
git clone <repo-url>
cd wa-dev-tools

# Установить зависимости
npm install

# Создать файл конфигурации
cp .env.example .env
# Заполнить переменные (см. раздел Конфигурация)

# Создать базу данных
mysql -u root -p < schema.sql

# Запустить в режиме разработки
node server.js

# Или через PM2 в продакшне
pm2 start ecosystem.config.js

Сервер стартует на http://localhost:3000 (порт задаётся через PORT в .env).


Архитектура

Приложение построено на Express 5 с модульной маршрутизацией. Каждый инструмент изолирован в отдельном файле роутера. Общая библиотека (lib/) предоставляет auth middleware, подключение к БД, логгер и SSRF-защиту.

wa-dev-tools/
├── server.js              # Точка входа: Express, сессии, middleware, AdminJS
├── ecosystem.config.js    # PM2-конфигурация
├── lib/
│   ├── auth.js            # Middleware авторизации (список публичных путей)
│   ├── admin.js           # AdminJS: Sequelize-модели, маунт /admin
│   ├── db.js              # Пул соединений mysql2, CRUD-функции для users
│   ├── logger.js          # Структурированный логгер (stdout + файл)
│   └── ssrf.js            # Валидация URL, блокировка приватных IP и диапазонов
├── routes/
│   ├── api.js             # Публичный REST API (settings, tools, content)
│   ├── auth.js            # Регистрация, вход, выход, /auth/me
│   ├── compress.js        # Сжатие изображений (Sharp + Archiver)
│   ├── placeholder.js     # Генератор placeholder-изображений
│   ├── svgeditor.js       # SVG-оптимизация и AI-генерация иконок
│   ├── video.js           # Конвертер видео (FFmpeg, асинхронные задачи)
│   ├── pdf.js             # PDF-инструменты (pdf-lib, Ghostscript)
│   ├── parser.js          # Парсер статей (Readability, JSDOM, кеш)
│   ├── httpclient.js      # HTTP-клиент (прокси-запросы, история сессии)
│   ├── redirects.js       # Анализатор цепочек редиректов
│   ├── pages.js           # HTML-страницы инструментов (formatter, md и др.)
│   ├── status.js          # Страница статуса сервисов
│   └── logs.js            # Просмотр логов (защищённый)
└── public/
    ├── shared.js          # Сайдбар, темы, список инструментов (клиент)
    ├── shared.css         # Базовые стили
    ├── landing.html       # Публичная лендинг-страница
    ├── login.html         # Страница входа
    ├── register.html      # Страница регистрации
    └── *.html             # Страницы инструментов

Порядок middleware в server.js

helmet (security headers)
↓
express-session
↓
express.static (public/) — всегда публично
↓
/auth/* — публично (вход/регистрация)
↓
/api/settings, /api/tools, /api/content/* — публично (лендинг)
↓
/status — публично
↓
authMiddleware — всё ниже требует сессии
↓
/api/*, /compress/*, /video/*, /pdf/*, /parser, ...

Инструменты

Изображения

Инструмент Путь Описание
Конвертер изображений /compress Пакетное сжатие и конвертация до 50 файлов. Форматы: JPEG, PNG, WebP, AVIF, TIFF, GIF. Настройка качества и максимальной стороны. Результат — ZIP-архив.
Placeholder /placeholder Генератор заглушек для макетов. API-эндпоинт /placeholder/:size/:bg/:fg/:text возвращает PNG/JPEG/WebP без авторизации.
SVG-редактор /svgeditor Оптимизация SVG (удаление комментариев, metadata, пустых групп, обрезка чисел). Генерация простых иконок по ключевому слову (20 встроенных шаблонов).
Фоторедактор /editor Клиентский редактор изображений.
Видеоконвертер /video Конвертация, сжатие, извлечение аудио и создание GIF через FFmpeg. Асинхронная обработка с поллингом прогресса.

Код

Инструмент Путь Описание
Code Formatter /formatter Форматирование кода на стороне клиента.
HTML Sanitizer /sanitizer Очистка HTML от потенциально опасных тегов и атрибутов.
Конвертер форматов /converter Конвертация между форматами данных (JSON, YAML, TOML и др.).

Веб

Инструмент Путь Описание
Парсер статей /parser Извлечение текста и метаданных из URL через Mozilla Readability. Поддержка кодировок (iconv-lite), кеш 50 записей / 10 мин, автоматическая смена кодировки.
HTTP-клиент /httpclient Прокси-запросы к внешним URL: GET/POST/PUT/DELETE, произвольные заголовки, тело запроса, таймаут до 60 сек. История запросов в сессии (до 30 записей).
Redirect-анализатор /redirects Трассировка цепочки редиректов до 15 шагов. Три User-Agent профиля: desktop, mobile, Googlebot. Детектирует длинные цепочки, смешанный протокол HTTP→HTTPS, временные 302-редиректы.

Утилиты

Инструмент Путь Описание
Генератор паролей /password Клиентская генерация паролей с настройкой сложности.
Markdown Viewer /md Рендер Markdown в HTML.
PDF-инструменты /pdf Merge, Split, Rotate, Delete pages, Reorder, Watermark, Page numbers, Compress (Ghostscript), Protect (пароль), Extract text, PDF→Images, Images→PDF.

Авторизация

Используется локальная авторизация на основе email + пароль.

Технический стек: bcrypt (10 rounds), express-session, MariaDB.

Процесс входа

  1. Пользователь отправляет POST /auth/login с email и password.
  2. Пароль сравнивается с password_hash в таблице users через bcrypt.compare.
  3. При успехе сессия перегенерируется (req.session.regenerate) для защиты от session fixation.
  4. В сессии сохраняется объект { id, email, name, role }.

Процесс регистрации

POST /auth/register
Body: { email, password, password2, name }

Валидация: формат email, минимум 6 символов пароля, совпадение паролей, уникальность email. Имя пользователя очищается от HTML-тегов.

Сессия

// Конфигурация сессии (server.js)
{
  secret: SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: {
    maxAge: 86400000,   // 24 часа
    httpOnly: true,
    secure: true,       // только в production
    sameSite: 'lax',
  }
}

Роли

Роль Доступ
user Все инструменты платформы
admin Все инструменты + ссылка на /admin в сайдбаре + доступ к AdminJS

Заблокированные аккаунты (is_blocked = true) получают 403 при попытке входа.

Публичные пути (без авторизации)

  • / — лендинг
  • /auth/* — вход, регистрация, выход
  • /status — страница статуса
  • /placeholder, /placeholder-img/* — API заглушек
  • /api/settings, /api/tools, /api/content/advantages, /api/content/dashboard — данные для лендинга
  • /health — health check

Админ-панель

Доступна по адресу /admin. Требует роль admin в основной сессии сайта — отдельная авторизация AdminJS не используется.

Построена на AdminJS 7 + @adminjs/sequelize. Модели подключены через Sequelize (параллельно с основным пулом mysql2).

Управляемые сущности

Раздел Модель Таблица Что можно делать
Пользователи User users Просмотр, создание, редактирование, блокировка, смена роли и пароля
Контент Category categories CRUD категорий инструментов
Контент Tool tools CRUD инструментов: включение/отключение, порядок сортировки, иконки
Контент ContentBlock content_blocks Редактирование текстовых блоков лендинга (richtext)
Настройки Setting settings Редактирование значений настроек приложения

Смена пароля через AdminJS: пароль хешируется bcrypt'ом в before-хуке действия edit / new. Поле password_hash скрыто из интерфейса.


API Reference

Публичные эндпоинты

Метод Путь Описание
GET /api/settings Все настройки как объект { key: value }
GET /api/tools Категории с вложенными активными инструментами
GET /api/content/advantages Блоки контента секции advantages
GET /api/content/dashboard Блоки контента секции dashboard
GET /placeholder/:size/:bg/:fg Изображение-заглушка
GET /placeholder/:size/:bg/:fg/:text Изображение-заглушка с текстом
GET /status/api Статус сервисов
GET /health Uptime, память, версия Node

Пример запроса к placeholder API:

GET /placeholder/800x600/0054e6/ffffff/Hello

Параметры: size = WxH, bg и fg = hex-цвет без #, text = текст на изображении. Формат: ?format=png|jpg|webp.

Защищённые эндпоинты (требуют сессию)

Авторизация:

Метод Путь Описание
POST /auth/login Вход. Body: { email, password }
POST /auth/register Регистрация. Body: { email, password, password2, name }
GET /auth/logout Выход, уничтожение сессии
GET /auth/me Текущий пользователь { id, email, name, role }

Изображения:

Метод Путь Описание
POST /compress Загрузка файлов (multipart/form-data, поле images[]). Параметры: format, quality, resize. Ответ: { downloadUrl, stats[] }
GET /download/:filename Скачать ZIP с результатом

Видео:

Метод Путь Описание
POST /video/upload Загрузка видео (до 200MB). Ответ: { jobId, info }
POST /video/convert Запуск обработки. Body: { jobId, mode, format, quality, startTime, endTime }
GET /video/progress/:jobId Прогресс задачи. Ответ: { status, progress, downloadUrl?, savings? }
GET /video/download/:filename Скачать результат

Режимы конвертации (mode): convert, compress, audio, gif.

PDF:

Метод Путь Описание
POST /pdf/upload Загрузка PDF/изображений (до 50MB, до 10 файлов). Ответ: { files: [{ id, name, size, pages }] }
GET /pdf/info/:fileId Метаданные PDF (страницы, автор, создатель)
POST /pdf/merge Объединение. Body: { fileIds: [] }
POST /pdf/split Разбивка страниц. Body: { fileId, ranges: "1-3,5" }
POST /pdf/rotate Поворот. Body: { fileId, pages: "all" или "1,3", angle: 90 }
POST /pdf/delete Удаление страниц. Body: { fileId, pages: "2,4" }
POST /pdf/reorder Переупорядочивание. Body: { fileId, order: [3,1,2] }
POST /pdf/watermark Водяной знак. Body: { fileId, text, fontSize, opacity, color }
POST /pdf/pagenumbers Нумерация страниц. Body: { fileId, position, startFrom }
POST /pdf/compress Сжатие через Ghostscript. Body: `{ fileId, quality: "screen"
POST /pdf/protect Защита паролем. Body: { fileId, password }
POST /pdf/extract-text Извлечение текста. Body: { fileId }
POST /pdf/toImages PDF → ZIP с PNG/JPEG. Body: { fileId, format, dpi }
POST /pdf/fromImages Изображения → PDF (multipart/form-data, поле images[])
GET /pdf/download/:filename Скачать результат

Парсер статей:

Метод Путь Описание
GET /parse?url= Полный разбор статьи: заголовок, автор, текст, HTML, изображения, ссылки
GET /metadata?url= Только метаданные (OG, description, дата)
GET /text?url= Только текст (text/plain)
GET /preview?url= Карточка превью: заголовок, описание, изображение

HTTP-клиент:

Метод Путь Описание
POST /api/proxy Прокси-запрос. Body: { url, method, headers, body, timeout }
GET /api/history История запросов текущей сессии
POST /api/history/add Добавить запись в историю
DELETE /api/history Очистить историю

Redirect-анализатор:

Метод Путь Описание
POST /api/redirect-analyze Трассировка. Body: `{ url, userAgent: "desktop"

SVG-редактор:

Метод Путь Описание
POST /api/svg-optimize Оптимизация SVG. Body: { svg: "..." }. Ответ: { svg, saved: % }
POST /api/svg-ai Генерация иконки. Body: `{ keyword, style: "outline"

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

HTTP-заголовки

Helmet добавляет стандартные security headers. Политика CSP отключена (используется Tailwind CDN с inline-скриптами).

Rate Limiting (express-rate-limit)

Эндпоинт Лимит
/auth/login, /auth/register 10 запросов / 15 мин
/compress Настраивается через RATE_LIMIT_MAX / RATE_LIMIT_WINDOW_MS (по умолчанию 30 / 60 сек)
/parse, /metadata, /text, /preview 20 запросов / 60 сек
/api/proxy Настраивается через PROXY_RATE_LIMIT_MAX (по умолчанию 60 / 60 сек)
/api/redirect-analyze 20 запросов / 60 сек
/video/upload 10 запросов / 60 сек
/pdf/* 30 запросов / 60 сек

SSRF-защита

Все эндпоинты, выполняющие server-side запросы к внешним URL (парсер, HTTP-клиент, redirect-анализатор), проходят валидацию через lib/ssrf.js:

  • Разрешены только протоколы http: и https:
  • Блокируются прямые обращения к приватным IP-диапазонам: 10.x, 172.16-31.x, 192.168.x, 127.x, 169.254.x, IPv6 loopback и link-local
  • Блокируются hostname'ы: localhost, *.local, *.internal
  • DNS-резолвинг: все A-записи домена проверяются на принадлежность к приватным диапазонам

Прочее

  • Сессионные cookie: httpOnly, secure (в production), sameSite: lax
  • Session fixation: при входе сессия перегенерируется через req.session.regenerate
  • Загружаемые файлы изображений валидируются по MIME-типу и расширению
  • Временные файлы автоматически удаляются: загрузки через 10 мин, результаты через 30 мин
  • Имена файлов транслитерируются и очищаются от спецсимволов перед записью в архив

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

Файл .env в корне проекта. Пример структуры:

# Сервер
NODE_ENV=production
PORT=3000

# База данных (MariaDB)
DB_HOST=localhost
DB_USER=wa_tools
DB_PASSWORD=секрет
DB_NAME=wa_tools

# Сессия
SESSION_SECRET=длинная-случайная-строка

# Логирование
LOG_LEVEL=info          # error | warn | info | debug

# Загрузка файлов (изображения)
MAX_FILE_SIZE_MB=20     # максимальный размер одного файла
MAX_FILES=50            # максимальное количество файлов в одном запросе

# Сжатие изображений
COMPRESS_QUALITY=80     # качество по умолчанию (1-100)

# Rate limiting (изображения)
RATE_LIMIT_WINDOW_MS=60000
RATE_LIMIT_MAX=30

# Rate limiting (HTTP-клиент)
PROXY_RATE_LIMIT_MAX=60
Переменная Описание По умолчанию
NODE_ENV Режим работы. В production cookie становятся secure development
PORT Порт Express-сервера 3000
DB_HOST Хост MariaDB localhost
DB_USER Пользователь БД wa_tools
DB_PASSWORD Пароль БД
DB_NAME Имя базы данных wa_tools
SESSION_SECRET Секрет для подписи сессионных cookie change-me-in-env
LOG_LEVEL Уровень логирования info
MAX_FILE_SIZE_MB Лимит размера изображения 20
MAX_FILES Лимит файлов в пакетной загрузке 50
COMPRESS_QUALITY Качество сжатия по умолчанию 80
RATE_LIMIT_WINDOW_MS Окно rate limiting (мс) 60000
RATE_LIMIT_MAX Лимит запросов в окне 30
PROXY_RATE_LIMIT_MAX Лимит для HTTP-клиента 60

Важно: Никогда не коммитьте .env в репозиторий. Значение SESSION_SECRET должно быть случайной строкой длиной не менее 32 символов.


Деплой

PM2

Конфигурация в ecosystem.config.js:

{
  name: 'images',
  script: 'server.js',
  cwd: '/mnt/webdata/www/images.wadevelop.ru',
  instances: 1,
  exec_mode: 'fork',
  max_memory_restart: '256M',
  restart_delay: 3000,
  max_restarts: 10,
  env: { NODE_ENV: 'production' }
}
# Запуск
pm2 start ecosystem.config.js

# Перезапуск после обновления
pm2 restart images

# Логи
pm2 logs images

Graceful shutdown

Сервер перехватывает SIGTERM и SIGINT: закрывает HTTP-сервер, ждёт завершения активных соединений до 10 секунд, затем завершается принудительно.

Nginx

Пример конфигурации виртуального хоста:

server {
    listen 443 ssl;
    server_name images.wadevelop.ru;

    ssl_certificate     /etc/letsencrypt/live/images.wadevelop.ru/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/images.wadevelop.ru/privkey.pem;

    client_max_body_size 210M;  # чуть больше MAX видео (200MB)

    location / {
        proxy_pass         http://127.0.0.1:3000;
        proxy_set_header   Host $host;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_read_timeout 120s;
    }
}

В server.js установлено app.set('trust proxy', 1) — необходимо для корректной работы rate limiting и логирования IP за nginx.

SSL

certbot --nginx -d images.wadevelop.ru

Мониторинг

Страница статуса

URL: /status — публичная HTML-страница в стиле status.claude.com.

API: GET /status/api — возвращает статус пяти сервисов:

{
  "overall": "operational",
  "uptime": 86400,
  "services": [
    { "name": "API", "status": "operational" },
    { "name": "Database", "status": "operational" },
    { "name": "Image Processing", "status": "operational" },
    { "name": "Video Processing", "status": "operational" },
    { "name": "Authentication", "status": "operational" }
  ],
  "timestamp": "2026-03-22T10:00:00.000Z"
}

Статус Database проверяется реальным запросом SELECT 1 к MariaDB.

Health Endpoint

URL: GET /health — расширенная информация о процессе (не требует авторизации):

{
  "status": "ok",
  "uptime": 3600,
  "memory": {
    "rss": "128MB",
    "heap": "64/128MB"
  },
  "node": "v22.0.0",
  "pid": 12345
}

Логи

Структурированный вывод в stdout (формат: [ISO timestamp] [LEVEL] message {meta}). PM2 пишет stdout в /home/treamz/.pm2/logs/images-out.log, stderr — в images-error.log.

Операции сжатия изображений дополнительно пишутся в файл compress.log в корне проекта (через log.logToFile).


Технологии

Компонент Технология Назначение
Runtime Node.js 18+ Серверная среда выполнения
Framework Express 5 HTTP-сервер, маршрутизация
База данных MariaDB 10.6+ Хранение пользователей, настроек, контента
ORM Sequelize 6 Модели для AdminJS
DB Driver mysql2 Прямые SQL-запросы (пул соединений)
Авторизация bcrypt + express-session Хеширование паролей, серверные сессии
Админ-панель AdminJS 7 + @adminjs/sequelize Веб-интерфейс управления данными
Изображения Sharp Конвертация, сжатие, генерация placeholder
Видео FFmpeg + ffprobe Конвертация, сжатие, извлечение аудио, GIF
PDF pdf-lib Merge, split, rotate, watermark, protect
PDF pdf-parse Извлечение текста и метаданных
PDF Ghostscript Сжатие, конвертация PDF в изображения
Парсер @mozilla/readability + JSDOM Извлечение контента статей
Кодировки iconv-lite Декодирование страниц в non-UTF-8
Загрузка файлов Multer Обработка multipart/form-data
Архивация Archiver ZIP-архивы с результатами
Геолокация geoip-lite Определение страны по IP в логах
Безопасность Helmet HTTP security headers
Rate Limiting express-rate-limit Ограничение частоты запросов
Шрифты SVG opentype.js Работа с шрифтами (вспомогательная)
UI Tailwind CSS (CDN) Стилизация интерфейса
Шрифты UI JetBrains Mono, Manrope Типографика
Процесс PM2 Управление процессом в production
Веб-сервер Nginx Reverse proxy, SSL termination
SSL Let's Encrypt (certbot) TLS-сертификаты

База данных

Схема таблиц

users — учётные записи пользователей

Поле Тип Описание
id INT PK AI Идентификатор
email VARCHAR(255) UNIQUE Email-адрес
password_hash VARCHAR(255) bcrypt-хеш пароля
display_name VARCHAR(100) Отображаемое имя
role ENUM('user','admin') Роль, по умолчанию user
is_blocked BOOLEAN Блокировка аккаунта
created_at DATETIME Дата регистрации
last_login DATETIME Дата последнего входа

settings — настройки приложения (key-value)

Поле Тип Описание
id INT PK AI Идентификатор
setting_key VARCHAR(100) UNIQUE Ключ настройки
setting_value TEXT Значение
description VARCHAR(255) Описание для AdminJS
updated_at DATETIME Дата обновления

categories — категории инструментов

Поле Тип Описание
id INT PK AI Идентификатор
slug VARCHAR(50) UNIQUE URL-идентификатор
title VARCHAR(100) Название
description VARCHAR(255) Описание
icon_svg TEXT SVG-иконка категории
color VARCHAR(20) Акцентный цвет (hex)
sort_order INT Порядок отображения

tools — инструменты платформы

Поле Тип Описание
id INT PK AI Идентификатор
slug VARCHAR(50) UNIQUE URL-идентификатор
title VARCHAR(100) Название
description VARCHAR(255) Описание
path VARCHAR(100) URL-путь инструмента
icon_svg TEXT SVG-иконка
category_id INT FK Ссылка на categories.id
sort_order INT Порядок в категории
is_enabled BOOLEAN Включён/отключён

content_blocks — текстовые блоки лендинга

Поле Тип Описание
id INT PK AI Идентификатор
block_key VARCHAR(100) UNIQUE Уникальный ключ блока
title VARCHAR(255) Заголовок
body TEXT HTML-содержимое (richtext)
section VARCHAR(50) Секция лендинга (advantages, dashboard и др.)
sort_order INT Порядок в секции
is_visible BOOLEAN Отображать/скрыть
updated_at DATETIME Дата последнего изменения