caps_enhancer/modules/auto-login.js
2026-03-23 16:47:34 +03:00

229 lines
10 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ==================== МОДУЛЬ АВТОЛОГИНА ====================
(function() {
const core = window.VideoIPRedirect;
if (!core) {
console.error('[VideoIPRedirect] CORE не загружен!');
return;
}
const debugLog = (msg, ...args) => core.debugLog('autologin', msg, ...args);
debugLog('🔐 Загрузка модуля...');
// Получить профиль по индексу
function getProfile(index) {
const profiles = core.CONFIG.autologin.profiles;
if (!Array.isArray(profiles) || profiles.length === 0) return null;
const idx = (typeof index === 'number' && index >= 0 && index < profiles.length) ? index : 0;
return profiles[idx] || profiles[0];
}
// Найти правило подсети для текущего хоста
function getSubnetRule() {
const host = window.location.hostname;
const rules = core.CONFIG.autologin.subnetRules || [];
return rules.find(r => r.subnet && host.startsWith(r.subnet)) || null;
}
// Проверка страницы логина типа 1 (порт 5000, /login, поля username/password)
function isLoginPage1() {
if (window.location.port !== '5000') return false;
if (!window.location.pathname.includes('/login')) return false;
const usernameField = document.querySelector('input[name="username"]');
const passwordField = document.querySelector('input[name="password"]');
return !!(usernameField && passwordField);
}
// Проверка страницы логина типа 2 (auth-logo.png + react-notification-root + noscript)
function isLoginPage2() {
const hasNoscript = Array.from(document.querySelectorAll('noscript'))
.some(n => n.textContent.includes('You need to enable JavaScript to run this app'));
if (!hasNoscript) return false;
const hasNotificationRoot = !!document.querySelector('div.react-notification-root, div[class*="react-notification-root"]');
if (!hasNotificationRoot) return false;
const hasAuthLogo = !!document.querySelector('img[src="/images/auth-logo.png"]');
if (!hasAuthLogo) return false;
return true;
}
// Заполнить форму логина данными профиля и нажать кнопку входа
function performLoginWithProfile(profile) {
if (!profile || !profile.username || !profile.password) {
debugLog('⚠️ Профиль не задан или пустые учётные данные');
return false;
}
const usernameField = document.querySelector('input[name="username"]');
const passwordField = document.querySelector('input[name="password"]');
const submitButton = document.querySelector('button[type="submit"]');
if (!usernameField || !passwordField) {
debugLog('❌ Не удалось найти поля ввода');
return false;
}
debugLog(`✍️ Заполнение полей (профиль: ${profile.name || 'без имени'})...`);
// React перехватывает нативный сеттер value — используем его напрямую,
// чтобы обновление дошло до React-state компонента
const nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
nativeSetter.call(usernameField, profile.username);
usernameField.dispatchEvent(new Event('input', { bubbles: true }));
nativeSetter.call(passwordField, profile.password);
passwordField.dispatchEvent(new Event('input', { bubbles: true }));
debugLog('✅ Поля заполнены');
const autoSubmit = typeof profile.autoSubmit === 'boolean' ? profile.autoSubmit : true;
if (autoSubmit && submitButton) {
const delay = profile.delay || 500;
debugLog(`⏱️ Вход через ${delay}мс...`);
setTimeout(() => {
debugLog('🚀 Нажатие кнопки "Вход"');
submitButton.click();
}, delay);
} else {
debugLog(' Автоматический вход отключен');
}
return true;
}
// Главная функция автологина для страницы типа 1 (с retry-логикой)
function performAutoLogin() {
debugLog('========== ПРОВЕРКА АВТОЛОГИНА (тип 1) ==========');
if (!core.CONFIG.autologin.enabled) {
debugLog('⏹ Автологин отключен в настройках');
return false;
}
if (!isLoginPage1()) {
debugLog('❌ Это не страница логина типа 1');
return false;
}
debugLog('✅ Обнаружена страница логина типа 1');
// Проверяем правила подсетей
const rule = getSubnetRule();
if (rule) {
debugLog(`📋 Найдено правило подсети: "${rule.subnet}", действие: "${rule.action}"`);
if (rule.action === 'skip') {
debugLog('⏭ Автологин пропущен по правилу подсети');
return false;
}
if (rule.action === 'profile') {
const overrideProfile = getProfile(rule.profileIndex);
debugLog(`🔄 Используем профиль по правилу подсети: ${overrideProfile?.name}`);
return performLoginWithProfile(overrideProfile);
}
}
const profile = getProfile(core.CONFIG.autologin.profilePage1);
debugLog(`🔑 Профиль для типа 1: ${profile?.name}`);
return performLoginWithProfile(profile);
}
// Автологин для страницы типа 1 — с повторными попытками
function tryAutoLogin(attempts = 3) {
debugLog(`🔄 Попытка автологина типа 1 (осталось попыток: ${attempts})`);
const success = performAutoLogin();
if (!success && attempts > 0) {
setTimeout(() => tryAutoLogin(attempts - 1), 500);
}
}
// Автологин для страницы типа 2 — одна попытка, без retry
function performAutoLoginPage2() {
debugLog('========== ПРОВЕРКА АВТОЛОГИНА (тип 2) ==========');
if (!core.CONFIG.autologin.enabled) {
debugLog('⏹ Автологин отключен в настройках');
return false;
}
if (!isLoginPage2()) {
debugLog('❌ Это не страница логина типа 2');
return false;
}
debugLog('✅ Обнаружена страница логина типа 2');
// Проверяем правила подсетей
const rule = getSubnetRule();
if (rule) {
debugLog(`📋 Найдено правило подсети: "${rule.subnet}", действие: "${rule.action}"`);
if (rule.action === 'skip') {
debugLog('⏭ Автологин пропущен по правилу подсети');
return false;
}
if (rule.action === 'profile') {
const overrideProfile = getProfile(rule.profileIndex);
debugLog(`🔄 Используем профиль по правилу подсети: ${overrideProfile?.name}`);
return performLoginWithProfile(overrideProfile);
}
}
const profile = getProfile(core.CONFIG.autologin.profilePage2);
debugLog(`🔑 Профиль для типа 2: ${profile?.name}`);
return performLoginWithProfile(profile);
// Намеренно без retry — одна попытка
}
// Однократный вход по кнопке из попапа (без проверки enabled, всегда отправка формы)
function performLoginOnce() {
if (!isLoginPage1() && !isLoginPage2()) {
return { ok: false, error: 'not_login_page' };
}
const isPage2 = isLoginPage2();
const profileIndex = isPage2
? core.CONFIG.autologin.profilePage2
: core.CONFIG.autologin.profilePage1;
const profile = getProfile(profileIndex);
if (!profile || !profile.username || !profile.password) {
return { ok: false, error: 'no_credentials' };
}
const usernameField = document.querySelector('input[name="username"]');
const passwordField = document.querySelector('input[name="password"]');
if (!usernameField || !passwordField) {
return { ok: false, error: 'no_fields' };
}
const nativeSetter2 = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
nativeSetter2.call(usernameField, profile.username);
usernameField.dispatchEvent(new Event('input', { bubbles: true }));
nativeSetter2.call(passwordField, profile.password);
passwordField.dispatchEvent(new Event('input', { bubbles: true }));
const submitButton = document.querySelector('button[type="submit"]');
if (submitButton) {
const delay = profile.delay || 500;
setTimeout(() => submitButton.click(), delay);
}
return { ok: true, profileName: profile.name || '' };
}
// Публичный API
window.VideoIPRedirect.AutoLogin = {
tryAutoLogin: tryAutoLogin,
performAutoLoginPage2: performAutoLoginPage2,
isLoginPage1: isLoginPage1,
isLoginPage2: isLoginPage2,
performLoginOnce: performLoginOnce,
getProfile: getProfile,
getSubnetRule: getSubnetRule
};
debugLog('✅ Модуль загружен');
})();