Расскажу, и сразу важное уточнение: для headless-наблюдателя вручную JWT доставать не нужно. В .env кладём DICECHESS_USERNAME + DICECHESS_PASSWORD, а Playwright сам логинится и читает JWT из storage (ровно тем же способом, что ниже, только автоматически). Токен живёт недолго — хардкодить его в .env смысла нет, он протухнет. Но посмотреть его глазами полезно (понять формат и срок жизни), так что вот как.
Где лежит JWT после логина
Способ A — Network (самый надёжный: видно, что реально шлёт сайт).
- Залогинься на dicechess.com.
- F12 → вкладка Network → фильтр
api. - Кликни любой запрос
/api/...→ Headers → Request Headers → строкаauthorization: Bearer eyJ…. То, что послеBearer— и есть JWT.
Способ B — Application/Storage (где он хранится). F12 → Application (Chrome) → Local Storage и Session Storage → https://dicechess.com. Ищи значение, которое начинается с eyJ и содержит две точки (header.payload.signature). Имя ключа обычно token/jwt/accessToken/authToken/Authorization (иногда зашит внутри JSON-объекта).
Способ C — консоль (найдёт сам и напечатает). Вставь в DevTools-консоль на dicechess.com:
(() => {
const isJwt = v => typeof v === 'string' && /^(Bearer\s+)?eyJ[\w-]+\.[\w-]+\.[\w-]+$/.test(v.trim());
const hits = [];
for (const [name, store] of [['localStorage', localStorage], ['sessionStorage', sessionStorage]]) {
for (let i = 0; i < store.length; i++) {
const key = store.key(i), val = store.getItem(key);
if (isJwt(val)) hits.push([`${name}.${key}`, val.trim()]);
else if (val && val[0] === '{') {
try { for (const [k, x] of Object.entries(JSON.parse(val))) if (isJwt(x)) hits.push([`${name}.${key} → ${k}`, String(x).trim()]); } catch {}
}
}
}
for (const p of document.cookie.split(';')) { const v = decodeURIComponent(p.split('=').slice(1).join('=').trim()); if (isJwt(v)) hits.push(['cookie', v]); }
hits.length ? hits.forEach(([w, t]) => console.log(w, '\n', t, '\n')) : console.log('JWT не найден — залогинься и повтори');
})();Хочешь узнать срок жизни токена — вставь его на jwt.io (или раскодируй middle-часть) и посмотри claim exp (Unix-время). Это подскажет, как часто наблюдателю перелогиниваться.
Что реально нужно для сборки
Поскольку логин за Cloudflare, авто-логин я сделаю проще всего так: Playwright открывает сайт, заполняет форму логина в UI (твои креды из .env), жмёт вход, потом читает JWT из storage — селекторы подберу вживую. То есть от тебя нужен только заполненный .env с username/password, и я добью login-модуль и прогоню сквозняк.
Кстати — ты зашарил репо на GitHub: хочешь, привяжу remote и запушу текущий коммит e894e73? Если да — кинь URL репозитория (или подтверди имя rabestro/dicechess-observer), проверю и сделаю git push -u.
Вот сниппет — вставь в консоль на dicechess.com (залогиненным): он сам находит JWT, декодирует payload и печатает iat/exp, срок жизни и сколько осталось.
(() => {
const isJwt = v => typeof v === 'string' && /^(Bearer\s+)?eyJ[\w-]+\.[\w-]+\.[\w-]+$/.test(v.trim());
let tok = null;
for (const s of [localStorage, sessionStorage]) for (let i = 0; i < s.length && !tok; i++) {
const v = s.getItem(s.key(i));
if (isJwt(v)) tok = v.trim();
else if (v && v[0] === '{') { try { for (const x of Object.values(JSON.parse(v))) if (isJwt(x)) { tok = String(x).trim(); break; } } catch {} }
}
if (!tok) { console.log('JWT не найден — залогинься и повтори'); return; }
tok = tok.replace(/^Bearer\s+/i, '');
let b = tok.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');
b += '='.repeat((4 - b.length % 4) % 4);
const p = JSON.parse(atob(b));
const fmt = s => s ? new Date(s * 1000).toLocaleString() : '—';
console.log('payload:', p);
console.log('issued (iat):', fmt(p.iat));
console.log('expires (exp):', fmt(p.exp));
if (p.iat && p.exp) console.log('срок жизни :', ((p.exp - p.iat) / 3600).toFixed(1), 'ч (', ((p.exp - p.iat) / 86400).toFixed(1), 'дней )');
if (p.exp) console.log('осталось :', ((p.exp - Date.now() / 1000) / 3600).toFixed(1), 'ч');
})();На что смотреть:
exp— момент истечения;срок жизни=exp − iat. Это и есть, как часто придётся перелогиниваться вручную (Turnstile).- Если в payload есть что-то вроде
refresh/rt, или в Local Storage рядом лежит отдельный refresh-токен — скажи: тогда сессию можно продлевать автоматически без капчи, и ручной логин будет почти никогда.
Альтернатива вручную: скопировать токен и вставить на jwt.io — там exp/iat видно в разобранном payload.
Кинь, что покажет (срок жизни и есть ли refresh) — и подберём стратегию обновления сессии.