From 87f8c55da0a3ba9febf2ca0435b3ebe0b2815f6e Mon Sep 17 00:00:00 2001 From: treamz Date: Fri, 24 Jul 2026 16:45:55 +0300 Subject: [PATCH] Sync prod working changes: new tools (regex/favicon), queue/session/storage/ws libs, video/compress/pdf/redirects updates --- DOCS.md | 111 ++- EDITOR-DOC.md | 115 --- README.md | 4 +- SVGEDITOR-FEATURES.md | 250 ------ input.css | 3 + lib/logger.js | 32 +- lib/queue.js | 136 ++++ lib/session.js | 29 + lib/storage.js | 13 + lib/ws.js | 57 ++ package-lock.json | 1396 +++++++++++++++++++++++++++++++- package.json | 15 +- public/SVGEDITOR-FEATURES.md | 250 ------ public/compress.html | 726 ++++++++++------- public/converter.html | 2 +- public/dashboard.html | 260 +++--- public/editor.html | 2 +- public/favicon.html | 325 ++++++++ public/formatter.html | 2 +- public/httpclient.html | 117 ++- public/landing.html | 20 +- public/login.html | 12 +- public/md.html | 36 +- public/parser.html | 2 +- public/password.html | 2 +- public/pdf.html | 2 +- public/placeholder.html | 2 +- public/redirects.html | 107 ++- public/regex.html | 610 ++++++++++++++ public/register.html | 12 +- public/sanitizer.html | 2 +- public/shared.js | 6 +- public/status.html | 12 +- public/svgeditor.html | 2 +- public/vendor/tailwind.min.css | 1 + public/video.html | 47 +- routes/api.js | 137 ++++ routes/auth.js | 1 + routes/compress.js | 108 ++- routes/favicon.js | 173 ++++ routes/httpclient.js | 1 + routes/pages.js | 1 + routes/parser.js | 3 +- routes/pdf.js | 18 +- routes/redirects.js | 3 +- routes/video.js | 140 ++-- server.js | 57 +- tailwind.config.js | 28 + 48 files changed, 4108 insertions(+), 1282 deletions(-) delete mode 100644 EDITOR-DOC.md delete mode 100644 SVGEDITOR-FEATURES.md create mode 100644 input.css create mode 100644 lib/queue.js create mode 100644 lib/session.js create mode 100644 lib/storage.js create mode 100644 lib/ws.js delete mode 100644 public/SVGEDITOR-FEATURES.md create mode 100644 public/favicon.html create mode 100644 public/regex.html create mode 100644 public/vendor/tailwind.min.css create mode 100644 routes/favicon.js create mode 100644 tailwind.config.js diff --git a/DOCS.md b/DOCS.md index 73831c4..6b9365b 100644 --- a/DOCS.md +++ b/DOCS.md @@ -35,13 +35,17 @@ | Среда выполнения | Node.js 20 | | Веб-фреймворк | Express 5 | | База данных | MariaDB 10.11 | +| Хранение сессий | express-mysql-session (MariaDB) | +| Очередь задач | better-sqlite3 (SQLite WAL, `/mnt/webdata/storage/jobs.db`) | | Обработка изображений | Sharp 0.34 | -| Обработка видео | FFmpeg (системный), ffprobe | +| Обработка видео | FFmpeg 5.1.8 (libx264, libvpx, libmp3lame), ffprobe | | Обработка PDF | pdf-lib, pdf-parse, Ghostscript (системный) | | Парсинг HTML | @mozilla/readability, jsdom | | Авторизация | express-session, bcrypt | | Панель администратора | AdminJS 7 + @adminjs/sequelize | -| Стили | Tailwind CSS (CDN), кастомный shared.css | +| Стили | Tailwind CSS 3 (CLI build, `vendor/tailwind.min.css`) | +| WebSocket | ws (реалтайм прогресс конвертации) | +| Логирование | pino (структурированный JSON) | | Процесс-менеджер | PM2 | ### Сервер @@ -69,7 +73,11 @@ │ ├── auth.js # Middleware авторизации: список публичных путей, редиректы │ ├── admin.js # Настройка AdminJS: Sequelize модели, ресурсы, монтирование │ ├── db.js # Пул соединений MariaDB (mysql2/promise), хелперы для users -│ ├── logger.js # Логгер: stdout + запись в compress.log через logToFile +│ ├── logger.js # Логгер pino: структурированный JSON + logToFile +│ ├── queue.js # Очередь задач на SQLite (better-sqlite3, WAL mode) +│ ├── session.js # Persistent sessions (express-mysql-session → MariaDB) +│ ├── storage.js # Единые пути хранения: /mnt/webdata/storage/{uploads,results} +│ ├── ws.js # WebSocket сервер (ws) — реалтайм прогресс конвертации │ └── ssrf.js # Защита от SSRF: проверка IP-диапазонов, DNS-резолвинг │ ├── routes/ @@ -650,6 +658,10 @@ session({ ### Rate limiting для auth +Rate limit работает per-user: `keyGenerator` использует `req.session.user.id` для авторизованных пользователей, `req.ip` для анонимных. Это предотвращает ситуацию, когда один бот исчерпывает лимит для всех. + + + 15 минут, 10 попыток (применяется к `POST /auth/login` и `POST /auth/register`). --- @@ -961,12 +973,25 @@ session({ ```js helmet({ - contentSecurityPolicy: false, // отключено — Tailwind CDN требует inline-скриптов + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://mc.yandex.ru"], + styleSrc: ["'self'", "'unsafe-inline'"], + imgSrc: ["'self'", "data:", "https://mc.yandex.ru"], + connectSrc: ["'self'", "wss:", "ws:"], + fontSrc: ["'self'"], + objectSrc: ["'none'"], + frameAncestors: ["'none'"], + baseUri: ["'self'"], + formAction: ["'self'"], + }, + }, crossOriginEmbedderPolicy: false, }) ``` -Остальные политики helmet включены: HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy. +CSP включён. `unsafe-inline` необходим для inline ` + @@ -36,63 +113,51 @@
-
+
- +
-

- Перетащите файлы сюда или выберите +

+ Перетащите файлы сюда или выберите

- +

Файлы загружаются мгновенно, по одному

+
- - - - - - - -
+ +
- - - - - + + +
- - @@ -103,269 +168,382 @@
-
80%
-
- - - - - - -
- - -
+ + diff --git a/public/dashboard.html b/public/dashboard.html index 932c766..68d762d 100644 --- a/public/dashboard.html +++ b/public/dashboard.html @@ -4,69 +4,47 @@ WA Dev Tools — Панель - + -
-
@@ -78,142 +56,120 @@
-
-

- Добро пожаловать -

-

- 14 инструментов для веб-разработки: сжатие картинок и видео, - форматирование кода, тестирование API, генерация паролей и многое другое. +

Добро пожаловать

+

+ Инструменты для веб-разработки: сжатие картинок и видео, форматирование кода, тестирование API и многое другое.

- -
-
- -
- Изображения - 5 - сжатие, видео, редактирование и генерация -
-
- - -
-
- -
- Код - 3 - форматирование, очистка и конвертация -
- - - -
-
- -
- Веб - 3 - HTTP-клиент, парсер и анализ -
- - - -
-
- -
- Утилиты - 3 - пароли, PDF, Markdown -
- - +
diff --git a/public/editor.html b/public/editor.html index d4119ff..00f0f57 100644 --- a/public/editor.html +++ b/public/editor.html @@ -4,7 +4,7 @@ Code Editor — WA Dev Tools - + diff --git a/public/favicon.html b/public/favicon.html new file mode 100644 index 0000000..639e107 --- /dev/null +++ b/public/favicon.html @@ -0,0 +1,325 @@ + + + + + + Генератор Favicon — WA Dev Tools + + + + + + + + + +
+
+ + +
+ +
+ + +
+ + +
+
+ + + +
+

+ Перетащите изображение или выберите файл +

+

PNG или SVG рекомендуется · любой формат · до 5 МБ

+ +
+ +
+ + + + + + + + + + +
+
+ + + + + + + + + diff --git a/public/formatter.html b/public/formatter.html index 041f4a3..2e5eb33 100644 --- a/public/formatter.html +++ b/public/formatter.html @@ -4,7 +4,7 @@ Code Formatter — WA Dev Tools - + diff --git a/public/httpclient.html b/public/httpclient.html index 45fb638..449a794 100644 --- a/public/httpclient.html +++ b/public/httpclient.html @@ -4,7 +4,7 @@ HTTP Client — WA Dev Tools - + @@ -211,7 +211,22 @@ @media(max-width: 900px) { .panels-grid { grid-template-columns: 1fr; } } + + /* AI Security Audit */ + .ai-audit-btn { display: inline-flex; align-items: center; gap: 5px; padding: 4px 10px; border-radius: 6px; border: 1px solid var(--accent); color: var(--accent); background: transparent; font-size: 11px; font-weight: 700; font-family: 'JetBrains Mono', monospace; cursor: pointer; transition: all .2s; white-space: nowrap; } + .ai-audit-btn:hover { background: var(--accent); color: #fff; } + .ai-audit-btn:disabled { opacity: .5; cursor: wait; } + .ai-result-panel { margin-top: 8px; padding: 14px; background: var(--surface-700); border: 1px solid var(--surface-600); border-radius: 10px; font-size: 13px; line-height: 1.7; color: var(--text-primary); max-height: 400px; overflow-y: auto; } + .ai-result-panel h1,.ai-result-panel h2,.ai-result-panel h3 { font-weight: 700; margin: .6em 0 .3em; font-size: 1.1em; } + .ai-result-panel code { background: var(--surface-800); padding: 1px 5px; border-radius: 3px; font-family: 'JetBrains Mono', monospace; font-size: .9em; } + .ai-result-panel ul,.ai-result-panel ol { padding-left: 1.5em; margin: .3em 0; } + .ai-result-panel li { margin: .15em 0; } + .ai-result-panel p { margin: .3em 0; } + .ai-result-panel table { border-collapse: collapse; width: 100%; margin: .5em 0; font-size: .9em; } + .ai-result-panel th,.ai-result-panel td { border: 1px solid var(--surface-600); padding: 4px 8px; text-align: left; } + .ai-result-panel th { background: var(--surface-800); } + @@ -343,6 +358,15 @@
+ + + @@ -595,6 +619,20 @@ }); }; + // Store response data for AI audit + window._lastResponse = { + status: data.status, + statusText: data.statusText, + headers: data.headers, + body: (data.body || '').slice(0, 2000), + url: data.url || req.url, + method: req.method, + time: data.time, + }; + // Show AI audit button + document.getElementById('aiAuditWrap').classList.remove('hidden'); + document.getElementById('aiAuditResult').classList.add('hidden'); + addToHistory(req.method, req.url, data.status, data.time); } catch (err) { statusBar.innerHTML = `Ошибка соединения: ${escHtml(err.message)}`; @@ -700,6 +738,83 @@ renderHistory(); })(); + + +// ── AI Security Audit ───────────────────────────────────────────────────────── +async function aiSecurityAudit() { + const resp = window._lastResponse; + if (!resp) return; + + const btn = document.getElementById('aiAuditBtn'); + const resultDiv = document.getElementById('aiAuditResult'); + + btn.disabled = true; + btn.innerHTML = ' Анализирую...'; + resultDiv.classList.remove('hidden'); + resultDiv.innerHTML = ''; + + // Build headers summary + const hdrs = resp.headers || {}; + const hdrLines = Object.entries(hdrs).map(([k,v]) => k + ': ' + v).join('\n'); + + const payload = { + pattern: `${resp.method} ${resp.url} → ${resp.status} ${resp.statusText} + +Response Headers: +${hdrLines} + +Response Body (first 500 chars): +${(resp.body || '').slice(0, 500)}`, + flags: 'security-audit' + }; + + let fullText = ''; + + try { + const res = await fetch('/api/ai/explain', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload), + }); + + if (!res.ok) throw new Error('Ошибка ' + res.status); + + const reader = res.body.getReader(); + const decoder = new TextDecoder(); + let buffer = ''; + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + buffer += decoder.decode(value, { stream: true }); + const lines = buffer.split('\n'); + buffer = lines.pop(); + for (const line of lines) { + if (!line.startsWith('data: ')) continue; + const d = line.slice(6).trim(); + if (d === '[DONE]') continue; + try { + const parsed = JSON.parse(d); + if (parsed.error) throw new Error(parsed.error); + const delta = parsed.choices?.[0]?.delta?.content || ''; + if (delta) { + fullText += delta; + let md = fullText.replace(/^```[a-z]*\n?/gm, '').replace(/```\s*$/gm, '').trim(); + resultDiv.innerHTML = marked.parse(md); + } + } catch (e) { + if (e.message && !e.message.includes('JSON')) throw e; + } + } + } + if (!fullText.trim()) resultDiv.innerHTML = 'Пустой ответ'; + } catch (err) { + resultDiv.innerHTML = '' + (err.message || 'AI недоступен') + ''; + } finally { + btn.disabled = false; + btn.innerHTML = ' AI Security Audit'; + } +} + - + diff --git a/public/login.html b/public/login.html index ed98ce1..88ff158 100644 --- a/public/login.html +++ b/public/login.html @@ -4,16 +4,8 @@ Вход — WA Dev Tools - - + + \n\n\n' + body + '\n - - - -\n'; + const fullHtml = ` + + + +Markdown + + + +${body} + +`; const blob = new Blob([fullHtml], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); diff --git a/public/parser.html b/public/parser.html index 62e5a7d..7ce4fa7 100644 --- a/public/parser.html +++ b/public/parser.html @@ -4,7 +4,7 @@ Парсер статей — WA Dev Tools - + diff --git a/public/password.html b/public/password.html index 4965658..e287502 100644 --- a/public/password.html +++ b/public/password.html @@ -4,7 +4,7 @@ Генератор паролей — WA Dev Tools - + diff --git a/public/pdf.html b/public/pdf.html index dcc0f70..38711e8 100644 --- a/public/pdf.html +++ b/public/pdf.html @@ -4,7 +4,7 @@ WA Dev Tools — PDF инструменты - + diff --git a/public/placeholder.html b/public/placeholder.html index 358d9c8..b2f4958 100644 --- a/public/placeholder.html +++ b/public/placeholder.html @@ -4,7 +4,7 @@ Placeholder — WA Dev Tools - + diff --git a/public/redirects.html b/public/redirects.html index 288d3ed..cabf909 100644 --- a/public/redirects.html +++ b/public/redirects.html @@ -4,7 +4,7 @@ Redirect Analyzer — WA Dev Tools - + @@ -233,7 +233,19 @@ } .url-text a { color: #6b9fff; text-decoration: none; } .url-text a:hover { text-decoration: underline; } + + /* AI Security Audit */ + .ai-audit-btn { display: inline-flex; align-items: center; gap: 5px; padding: 6px 14px; border-radius: 8px; border: 1px solid var(--accent); color: var(--accent); background: transparent; font-size: 12px; font-weight: 700; font-family: 'JetBrains Mono', monospace; cursor: pointer; transition: all .2s; } + .ai-audit-btn:hover { background: var(--accent); color: #fff; } + .ai-audit-btn:disabled { opacity: .5; cursor: wait; } + .ai-result-panel { margin-top: 10px; padding: 14px; background: var(--surface-700); border: 1px solid var(--surface-600); border-radius: 10px; font-size: 13px; line-height: 1.7; color: var(--text-primary); } + .ai-result-panel h1,.ai-result-panel h2,.ai-result-panel h3 { font-weight: 700; margin: .6em 0 .3em; font-size: 1.1em; } + .ai-result-panel code { background: var(--surface-800); padding: 1px 5px; border-radius: 3px; font-family: 'JetBrains Mono', monospace; font-size: .9em; } + .ai-result-panel ul,.ai-result-panel ol { padding-left: 1.5em; margin: .3em 0; } + .ai-result-panel li { margin: .15em 0; } + .ai-result-panel p { margin: .3em 0; } + @@ -326,6 +338,15 @@ + + +
@@ -536,6 +557,12 @@ renderIssues(data); renderChain(data); renderHeaders(data); + + // Store for AI audit + window._lastRedirectData = data; + document.getElementById('aiAuditWrap').classList.remove('hidden'); + document.getElementById('aiAuditResult').classList.add('hidden'); + showState('result'); } catch (err) { @@ -599,6 +626,84 @@ if (qp.get('url')) { urlInput.value = qp.get('url'); analyze(); } })(); + + +// ── AI Security Audit for Redirects ─────────────────────────────────────────── +async function aiSecurityAudit() { + const data = window._lastRedirectData; + if (!data || !data.chain) return; + + const btn = document.getElementById('aiAuditBtn'); + const resultDiv = document.getElementById('aiAuditResult'); + + btn.disabled = true; + btn.innerHTML = ' Анализирую...'; + resultDiv.classList.remove('hidden'); + resultDiv.innerHTML = ''; + + // Build chain summary for AI + const chainInfo = data.chain.map((step, i) => { + const hdrs = Object.entries(step.headers || {}).map(([k,v]) => ' ' + k + ': ' + v).join('\n'); + return `Step ${i+1}: ${step.status} ${step.statusText} ${step.url}\n${hdrs}`; + }).join('\n\n'); + + const payload = { + pattern: `Redirect chain analysis: +Total steps: ${data.chain.length} +Total time: ${data.total_time}ms +Loop detected: ${data.loop_detected ? 'YES' : 'no'} +Issues: ${(data.issues || []).join(', ') || 'none'} + +${chainInfo}`, + flags: 'security-audit' + }; + + let fullText = ''; + + try { + const res = await fetch('/api/ai/explain', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload), + }); + if (!res.ok) throw new Error('Ошибка ' + res.status); + + const reader = res.body.getReader(); + const decoder = new TextDecoder(); + let buffer = ''; + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + buffer += decoder.decode(value, { stream: true }); + const lines = buffer.split('\n'); + buffer = lines.pop(); + for (const line of lines) { + if (!line.startsWith('data: ')) continue; + const d = line.slice(6).trim(); + if (d === '[DONE]') continue; + try { + const parsed = JSON.parse(d); + if (parsed.error) throw new Error(parsed.error); + const delta = parsed.choices?.[0]?.delta?.content || ''; + if (delta) { + fullText += delta; + let md = fullText.replace(/^```[a-z]*\n?/gm, '').replace(/```\s*$/gm, '').trim(); + resultDiv.innerHTML = marked.parse(md); + } + } catch (e) { + if (e.message && !e.message.includes('JSON')) throw e; + } + } + } + if (!fullText.trim()) resultDiv.innerHTML = 'Пустой ответ'; + } catch (err) { + resultDiv.innerHTML = '' + (err.message || 'AI недоступен') + ''; + } finally { + btn.disabled = false; + btn.innerHTML = ' AI Security Audit'; + } +} + + + + + + + + +
+
+ + +
+ +
+ + +
+ +
+ + +
+
Регулярное выражение
+
+ / + + / + g +
+
+ + +
+ Флаги: + + + + + +
+
+ + +
+
+
Тестовая строка
+
нет совпадений
+
+
+ + +
+
+ + +
+
Совпадения
+
+
Введите паттерн и текст для поиска
+
+
+ + +
+ + + +
+ + + +
+
+ +
+
+ + + + + + + + + diff --git a/public/register.html b/public/register.html index 63bdc31..3798584 100644 --- a/public/register.html +++ b/public/register.html @@ -4,16 +4,8 @@ Регистрация — WA Dev Tools - - + +