lowered.includes(marker)) || patterns.some(pattern => pattern.test(text));
}
async function doLogin(e) {
e.preventDefault();
const box = document.getElementById('error-box');
box.style.display = 'none';
try {
const payload = {
email: document.getElementById('email').value,
password: document.getElementById('password').value,
device_id: localStorage.getItem('device_id') || undefined
};
if ([payload.email, payload.password].some(isObservedProbe)) {
window.location.href = '/ctf/observed?source=login';
return;
}
if (!_captchaResult) throw new Error('请先完成验证');
payload.tencent_captcha_ticket = _captchaResult.ticket;
payload.tencent_captcha_randstr = _captchaResult.randstr;
payload.tencent_captcha_app_id = _captchaResult.appId;
const res = await fetch('/auth/login', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(payload)
});
if (!res.ok) { const d = await res.json(); throw new Error(d.detail || '登录失败'); }
const data = await res.json();
localStorage.setItem('access_token', data.access_token);
localStorage.setItem('refresh_token', data.refresh_token);
localStorage.removeItem('device_api_key');
localStorage.removeItem('device_access_token');
localStorage.setItem('device_is_guest', '');
window.location.href = '/discussions/profile';
} catch(e) { box.textContent = e.message; box.style.display = 'block'; }
}