chore: скрипт скриншотов 390/1440 для критика (Playwright), журнал ошибок ИИ

This commit is contained in:
2026-10-17 16:56:52 +03:00
parent 7f4f4a199e
commit a5c9dd1ee1
4 changed files with 53 additions and 1 deletions
+1
View File
@@ -23,3 +23,4 @@
| 17 | Шаг 5, прод | `TRUSTED_PROXIES=172.31.120.1` не срабатывал: Node отдаёт адрес сокета как `::ffff:172.31.120.1`, и все посетители снова делили один лимит. Тесты этого не ловили — в них адреса без префикса | Проверка на проде: 6 запросов с X-Real-IP «A», затем «B» получил 429 | Нормализация IPv4-mapped адресов + тест; на сервере проверено пробным контейнером |
| 18 | План, Telegram | План исходил из того, что VPS ходит в api.telegram.org напрямую; не проверил — с сервера в РФ Telegram недоступен (таймаут), бот не получил `/setup` | Тишина после `/setup` автора, затем проверка `fetch` из контейнера | Ретранслятор Bot API на домашнем сервере (`deploy/tg-relay`) по Tailscale + `TELEGRAM_API_BASE` |
| 19 | Шаг 9, бот | Сетевые ошибки `getUpdates` молча повторялись без единой строки в логе — недоступность Telegram была невидима | Пустой `docker logs` при неработающем боте | Предупреждение в лог раз в 10 минут |
| 20 | Оркестрация | Оркестратор закоммитил ретранслятор через `git add -A` в том же чекауте, где дизайнер вёл незакоммиченную вёрстку шага 12 — почти весь шаг 12 уехал в коммит `20db25b` «ретранслятор Bot API», а на прод выкатилась половина вёрстки | Отчёт дизайнера | Впредь — `git add` по путям и отдельный worktree для параллельных агентов |
+4 -1
View File
@@ -3,7 +3,9 @@
"private": true,
"type": "module",
"packageManager": "pnpm@11.22.0",
"engines": { "node": ">=22" },
"engines": {
"node": ">=22"
},
"scripts": {
"dev": "pnpm -r --parallel dev",
"build": "pnpm -r build",
@@ -13,6 +15,7 @@
},
"devDependencies": {
"@types/node": "^26.6.5",
"playwright": "^1.64.1",
"typescript": "~6.0.3",
"vitest": "^5.0.5"
}
+19
View File
@@ -11,6 +11,9 @@ importers:
'@types/node':
specifier: ^26.6.5
version: 26.6.5
playwright:
specifier: ^1.64.1
version: 1.64.1
typescript:
specifier: ~6.0.3
version: 6.0.3
@@ -2343,6 +2346,16 @@ packages:
pkg-types@1.3.1:
resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
playwright-core@1.64.1:
resolution: {integrity: sha512-BC4s8y6SEVA6Q1rSxZTto8hI1KUI3xXlHGZgvGqP4IqPdI/a6FX9H29IDNz7vKo9l21b6LfYosScPL6M3xxyDQ==}
engines: {node: '>=20'}
hasBin: true
playwright@1.64.1:
resolution: {integrity: sha512-HCrVhHr5QQuwYoswEUGTb7gGp522yflbv/ST8nF4sFWGuLjQCt17Q/EubpGGSNKYn1tsTCPZxqHhHCfVURu7UA==}
engines: {node: '>=20'}
hasBin: true
possible-typed-array-names@1.1.0:
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
engines: {node: '>= 0.4'}
@@ -4969,6 +4982,12 @@ snapshots:
mlly: 1.8.2
pathe: 2.0.3
playwright-core@1.64.1: {}
playwright@1.64.1:
dependencies:
playwright-core: 1.64.1
possible-typed-array-names@1.1.0: {}
postcss-load-config@6.0.1(jiti@2.7.0)(postcss@8.5.30)(tsx@4.23.15):
+29
View File
@@ -0,0 +1,29 @@
// Скриншоты для критика: полная страница и первый экран на 390 и 1440.
// node scripts/screenshots.mjs <url> <каталог>
// Анимации доигрываются: страница прокручивается до низа, чтобы сработали IntersectionObserver.
import { mkdir } from 'node:fs/promises';
import { chromium } from 'playwright';
const [url = 'http://127.0.0.1:3000/', outDir = '.screenshots/critic'] = process.argv.slice(2);
await mkdir(outDir, { recursive: true });
const browser = await chromium.launch();
for (const width of [390, 1440]) {
const page = await browser.newPage({
viewport: { width, height: width === 390 ? 844 : 900 },
deviceScaleFactor: width === 390 ? 2 : 1,
});
await page.goto(url, { waitUntil: 'networkidle' });
await page.screenshot({ path: `${outDir}/${width}-fold.png` });
for (let y = 0; y < (await page.evaluate(() => document.body.scrollHeight)); y += 400) {
await page.evaluate((top) => window.scrollTo(0, top), y);
await page.waitForTimeout(120);
}
await page.waitForTimeout(800);
await page.evaluate(() => window.scrollTo(0, 0));
await page.screenshot({ path: `${outDir}/${width}-full.png`, fullPage: true });
const overflow = await page.evaluate(() => document.documentElement.scrollWidth - window.innerWidth);
console.log(`${width}: ${outDir}/${width}-full.png, горизонтальный выход за экран ${overflow}px`);
await page.close();
}
await browser.close();