Files
portal/app/playwright/refresh-session.js
T
Дмитрий 0f6f38a70e @
fix(supplier): реальные endpoint'ы отчёта «Запрос номеров» (discovery T3)

Discovery T3 на живом supplier-портале crm.bp-gr.ru (Playwright MCP)
вскрыл фактические endpoint'ы вместо placeholder'ов из spec §4.3:

- POST /admin/report/save-report (JSON body, selectType=49 + reportFilter)
  — возвращает строку "OK", не JSON с id;
- GET  /admin/report/load-reports — массив отчётов, id извлекается
  title-match'ем «Запрос номеров с {from} по {to}»;
- GET  /admin/report/getfile?id=N — 302 redirect на отдельный
  download-host (oki.needcallbuy.ru), Laravel HTTP follows redirect.

SupplierPortalClient: requestNumbersReport/waitReportReady/downloadReport
переписаны под реальный контракт; request() +параметр asJson;
connectTimeout(30)+timeout(60) против flaky DNS resolve.

refresh-session.js: селекторы login-формы Yii2 — placeholder
input[name=login] → реальные #loginform-username/-password.

Тесты SupplierPortalClientReportTest + CsvReconcileJobTest адаптированы
под новый внутренний контракт. Pest 15/15, Larastan 0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@
2026-05-19 07:42:12 +03:00

93 lines
3.0 KiB
JavaScript

#!/usr/bin/env node
/**
* Headless Playwright login на crm.bp-gr.ru.
*
* Input (JSON через stdin):
* {login, password, url}
*
* Output (JSON через stdout):
* {phpsessid, csrf, refreshed_at}
*
* Exit codes:
* 0 — success
* 1 — auth failed (login/password rejected, или session cookie missing)
* 2 — DOM не найден (CSRF token не найден)
* 3 — timeout (60s)
* 4 — invalid input или другая ошибка
*/
const { chromium } = require('playwright');
const TIMEOUT_MS = 60_000;
async function refresh(args) {
const browser = await chromium.launch({ headless: true });
try {
const context = await browser.newContext();
const page = await context.newPage();
await page.goto(args.url, { waitUntil: 'load', timeout: TIMEOUT_MS });
// DOM-селекторы crm.bp-gr.ru/login (Yii2 LoginForm) — verified live 2026-05-19 через Playwright MCP.
const loginSelector = '#loginform-username';
const passwordSelector = '#loginform-password';
const submitSelector = 'button[type=submit]';
await page.fill(loginSelector, args.login);
await page.fill(passwordSelector, args.password);
await Promise.all([
page.waitForLoadState('networkidle', { timeout: TIMEOUT_MS }),
page.click(submitSelector),
]);
let csrf = null;
try {
csrf = await page.locator('meta[name=csrf-token]').first().getAttribute('content', { timeout: 5000 });
} catch (e) {
// CSRF meta tag not found — try other patterns в Task 1 discovery
}
const cookies = await context.cookies();
const sessionCookie = cookies.find(c => c.name === 'PHPSESSID' || c.name === 'JSESSIONID');
if (!sessionCookie) {
process.stderr.write(JSON.stringify({ error: 'session cookie not found in response' }));
process.exit(1);
}
if (!csrf) {
process.stderr.write(JSON.stringify({ error: 'CSRF token not found in DOM' }));
process.exit(2);
}
process.stdout.write(JSON.stringify({
phpsessid: sessionCookie.value,
csrf: csrf,
refreshed_at: new Date().toISOString(),
}));
process.exit(0);
} catch (err) {
process.stderr.write(JSON.stringify({ error: err.message }));
process.exit(err.message.includes('Timeout') ? 3 : 4);
} finally {
await browser.close();
}
}
// Read stdin
let input = '';
process.stdin.on('data', chunk => { input += chunk; });
process.stdin.on('end', () => {
let args;
try {
args = JSON.parse(input);
} catch (e) {
process.stderr.write(JSON.stringify({ error: 'invalid JSON on stdin' }));
process.exit(4);
}
if (!args.login || !args.password || !args.url) {
process.stderr.write(JSON.stringify({ error: 'missing required keys: login, password, url' }));
process.exit(4);
}
refresh(args);
});