229 lines
10 KiB
JavaScript
229 lines
10 KiB
JavaScript
// ==================== МОДУЛЬ АВТОЛОГИНА ====================
|
||
|
||
(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('✅ Модуль загружен');
|
||
})();
|