328 lines
24 KiB
HTML
328 lines
24 KiB
HTML
<!doctype html>
|
|
<html lang="ru">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>Auto Mirror Switcher</title>
|
|
<link rel="stylesheet" href="options.css">
|
|
</head>
|
|
<body>
|
|
<main class="page">
|
|
<header class="header">
|
|
<div>
|
|
<h1>Auto Mirror Switcher</h1>
|
|
<p>Автоматически переключает неработающие mirror-домены и сохраняет полный путь страницы.</p>
|
|
</div>
|
|
<label class="toggle switch-card">
|
|
<input id="globalEnabled" type="checkbox">
|
|
<span>Расширение включено</span>
|
|
</label>
|
|
</header>
|
|
|
|
<section class="card action-card">
|
|
<div class="row row-actions">
|
|
<button id="addRuleBtn" type="button">Добавить правило</button>
|
|
<button id="addMultclubBtn" type="button">Добавить preset Multclub</button>
|
|
<button id="exportSettingsBtn" type="button">Экспорт настроек</button>
|
|
<button id="importSettingsBtn" type="button">Импорт настроек</button>
|
|
<label class="checkline autosave-toggle">
|
|
<input id="autoSaveEnabled" type="checkbox">
|
|
<span>Автосохранение</span>
|
|
</label>
|
|
<button id="saveBtn" type="button" class="primary">Сохранить</button>
|
|
<input id="importFileInput" class="hidden" type="file" accept="application/json,.json">
|
|
</div>
|
|
|
|
</section>
|
|
|
|
<details class="card guide-card" id="guideSection">
|
|
<summary class="section-summary">
|
|
<div>
|
|
<h2>Как пользоваться</h2>
|
|
<p>Пошаговая настройка правил и объяснение того, когда расширение переключает зеркало.</p>
|
|
</div>
|
|
<div class="guide-badge save-reminder">Главное: после изменений нажимай «Сохранить»</div>
|
|
</summary>
|
|
|
|
<div class="section-body">
|
|
<div class="steps">
|
|
<div class="step">
|
|
<span class="step-num">1</span>
|
|
<div>
|
|
<strong>Включи расширение и правило</strong>
|
|
<p>Глобальный переключатель сверху включает обработку страниц. Внутри редактора у каждого правила есть отдельный чекбокс: выключенное правило не участвует в поиске зеркал.</p>
|
|
</div>
|
|
</div>
|
|
<div class="step">
|
|
<span class="step-num">2</span>
|
|
<div>
|
|
<strong>Опиши исходные адреса</strong>
|
|
<p>В поле RegExp укажи, какие host обрабатываются. Проверяется только host без протокола, пути, query и hash, например <code>mirror1.example.com</code>.</p>
|
|
</div>
|
|
</div>
|
|
<div class="step">
|
|
<span class="step-num">3</span>
|
|
<div>
|
|
<strong>Настрой кандидатов</strong>
|
|
<p>Выбери стратегию: числа в шаблоне, буквенные токены или готовый список host. Новый адрес всегда собирается из выбранного host и исходного пути страницы.</p>
|
|
</div>
|
|
</div>
|
|
<div class="step">
|
|
<span class="step-num">4</span>
|
|
<div>
|
|
<strong>Сохрани и проверь</strong>
|
|
<p>После сохранения открой страницу. Расширение сработает при сетевой ошибке, HTTP 5xx/403/451 или найденном маркере блокировки в тексте страницы.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<details>
|
|
<summary>Пример с условным доменом</summary>
|
|
<div class="details-body">
|
|
<p>Если открыта страница:</p>
|
|
<pre><code>https://mirror1.example.com/path/page.html?view=full#top</code></pre>
|
|
<p>и текущий host считается нерабочим, расширение подбирает следующий host по правилу и открывает тот же путь:</p>
|
|
<pre><code>https://mirror2.example.com/path/page.html?view=full#top
|
|
https://mirror3.example.com/path/page.html?view=full#top
|
|
https://mirror4.example.com/path/page.html?view=full#top</code></pre>
|
|
<p>Протокол берётся из настройки правила: всегда <code>https</code>, всегда <code>http</code> или такой же, как в исходном URL. Path, query и hash сохраняются без изменений.</p>
|
|
</div>
|
|
</details>
|
|
|
|
<details>
|
|
<summary>Примеры правил для example.com</summary>
|
|
<div class="details-body guide-columns">
|
|
<div>
|
|
<h3>Цифры в поддомене</h3>
|
|
<pre><code>hostRegex: ^mirror\d+\.example\.com$
|
|
template: mirror{n}.example.com
|
|
strategy: number</code></pre>
|
|
</div>
|
|
<div>
|
|
<h3>Цифры перед доменом</h3>
|
|
<pre><code>hostRegex: ^\d+\.example\.com$
|
|
template: {n}.example.com
|
|
strategy: number</code></pre>
|
|
</div>
|
|
<div>
|
|
<h3>Вариант без цифры</h3>
|
|
<pre><code>hostRegex: ^mirror\d*\.example\.com$
|
|
template: mirror{n}.example.com
|
|
strategy: number
|
|
includeEmpty: on</code></pre>
|
|
<p><code>\d+</code> означает «одна или больше цифр», а <code>\d*</code> разрешает отсутствие цифры. Включай вариант без цифры только если host вроде <code>mirror.example.com</code> тоже должен быть кандидатом.</p>
|
|
</div>
|
|
<div>
|
|
<h3>Буквы/токены</h3>
|
|
<pre><code>hostRegex: ^node-[a-z]+\.example\.com$
|
|
template: node-{s}.example.com
|
|
strategy: letters</code></pre>
|
|
</div>
|
|
<div>
|
|
<h3>Готовый список</h3>
|
|
<pre><code>mirror1.example.com
|
|
mirror2.example.com
|
|
mirror3.example.com</code></pre>
|
|
</div>
|
|
</div>
|
|
</details>
|
|
|
|
<details>
|
|
<summary>Что означают основные поля</summary>
|
|
<div class="details-body">
|
|
<dl class="field-help">
|
|
<dt>Какие host обрабатывать, RegExp</dt>
|
|
<dd>Фильтр host, на которых расширение должно включаться в режимах с шаблоном. Проверяется только host: например <code>mirror1.example.com</code>, без <code>https://</code>, пути и параметров. В режиме готового списка это поле скрывается: правило включается на host из списка.</dd>
|
|
<dt>Режим step</dt>
|
|
<dd>При ошибке сразу выбирает следующий кандидат из списка и открывает его в текущей вкладке. Кандидаты строятся по правилу, уже попробованные host пропускаются.</dd>
|
|
<dt>Режим probe</dt>
|
|
<dd>Сначала проверяет кандидатов фоновыми запросами, потом открывает первый ответивший host со статусом 2xx/3xx и без маркеров блокировки в тексте. Количество одновременных проверок задаёт поле «Параллельных проверок».</dd>
|
|
<dt>Последний рабочий host</dt>
|
|
<dd>Когда страница загрузилась без маркеров блокировки, content script отправляет сигнал <code>PAGE_OK</code>, и расширение запоминает этот host. Значение <strong>«Кэш рабочего host, часов» = 0</strong> означает «не забывать по времени»: сохранённый host используется, пока сам не даст ошибку или маркер блокировки.</dd>
|
|
<dt>Маркеры страницы блокировки/ошибки</dt>
|
|
<dd>Content script несколько раз после загрузки проверяет title и текст страницы. Если найден маркер, отправляется <code>BLOCKED_PAGE_DETECTED</code>, текущий host считается плохим, кэш для него очищается, и расширение ищет следующий вариант. Добавляй сюда точные фразы из страницы ошибки, по одной строке.</dd>
|
|
<dt>Максимум редиректов</dt>
|
|
<dd>Это защита от бесконечного перебора. Код автоматически поднимает эффективный лимит до размера списка кандидатов плюс запас, но ручное значение всё равно полезно для больших списков.</dd>
|
|
</dl>
|
|
</div>
|
|
</details>
|
|
|
|
<details>
|
|
<summary>Экспорт и импорт настроек</summary>
|
|
<div class="details-body">
|
|
<p><strong>Экспорт настроек</strong> скачивает JSON-файл со всеми правилами и глобальным переключателем расширения. Сохрани этот файл отдельно, чтобы быстро восстановить конфигурацию после переустановки Chrome или переноса расширения.</p>
|
|
<p><strong>Импорт настроек</strong> загружает такой JSON-файл обратно и заменяет текущие правила. После успешного импорта настройки сразу сохраняются в расширении.</p>
|
|
<p>Кэш последнего рабочего host не экспортируется: он зависит от текущего профиля Chrome и обновляется автоматически при реальном открытии страниц.</p>
|
|
</div>
|
|
</details>
|
|
|
|
<details>
|
|
<summary>Если не сработало</summary>
|
|
<div class="details-body">
|
|
<ul class="troubleshooting">
|
|
<li>Проверь, что расширение включено вверху страницы и на странице <code>chrome://extensions</code>.</li>
|
|
<li>После любых изменений нажми <strong>«Применить к правилу»</strong>, затем <strong>«Сохранить»</strong>. Если включено автосохранение, кнопка сохранения отключена, а изменения записываются автоматически.</li>
|
|
<li>Для режимов с шаблоном убедись, что <code>hostRegex</code> совпадает именно с host, например <code>mirror1.example.com</code>. В режиме готового списка проверь, что текущий host есть в списке.</li>
|
|
<li>Если рабочий номер вышел за диапазон, увеличь поле <strong>«До»</strong>. Поле <strong>«Макс. редиректов»</strong> автоматически расширяется до размера диапазона, но его всё равно можно поднять вручную для больших списков.</li>
|
|
<li>Если открылась страница ошибки с <code>Host Error</code>, <code>Error 522</code>, <code>Origin is unreachable</code>, <code>Cloudflare Ray ID</code> или похожим текстом и расширение не переключилось, добавь точную фразу из этой страницы в поле <strong>«Маркеры страницы блокировки/ошибки»</strong>.</li>
|
|
<li>Если расширение продолжает вести на старый сохранённый host, нажми «Очистить кэш правила» или открой именно проблемную страницу; при DNS/сетевой ошибке, HTTP 5xx/403/451 или найденном маркере блокировки кэш будет сброшен автоматически.</li>
|
|
<li>Для диагностики открой блок <strong>«Журнал диагностики»</strong> в этих настройках, нажми <strong>«Обновить журнал»</strong> и экспортируй JSON. Также можно открыть DevTools расширения на <code>chrome://extensions</code> и смотреть логи <code>[Auto Mirror Switcher]</code>.</li>
|
|
</ul>
|
|
</div>
|
|
</details>
|
|
</div>
|
|
</details>
|
|
<section class="grid">
|
|
<aside class="card">
|
|
<h2>Правила</h2>
|
|
<div id="rulesList" class="rules-list"></div>
|
|
</aside>
|
|
|
|
<section class="card editor-card">
|
|
<h2>Редактор</h2>
|
|
<form id="ruleForm">
|
|
<input id="ruleId" type="hidden">
|
|
|
|
<label class="checkline">
|
|
<input id="ruleEnabled" type="checkbox">
|
|
<span>Правило включено</span>
|
|
</label>
|
|
|
|
<label>
|
|
Название
|
|
<input id="name" type="text" placeholder="Например: Example mirrors">
|
|
</label>
|
|
|
|
<label id="hostRegexBlock">
|
|
Какие host обрабатывать, RegExp
|
|
<input id="hostRegex" type="text" placeholder="^mirror\d*\.example\.com$">
|
|
<small>Проверяется только host без протокола и пути. Например: <code>mirror10.example.com</code>.</small>
|
|
</label>
|
|
|
|
<div class="row">
|
|
<label>
|
|
Протокол для нового адреса
|
|
<select id="protocol">
|
|
<option value="https">https</option>
|
|
<option value="http">http</option>
|
|
<option value="same">как в исходном URL</option>
|
|
</select>
|
|
</label>
|
|
|
|
<label>
|
|
Режим при ошибке DNS/сети
|
|
<select id="mode">
|
|
<option value="step">step: открыть следующий вариант</option>
|
|
<option value="probe">probe: сначала проверить варианты в фоне</option>
|
|
</select>
|
|
</label>
|
|
</div>
|
|
|
|
<label>
|
|
Тип перебора
|
|
<select id="strategy">
|
|
<option value="number">Цифры в домене</option>
|
|
<option value="letters">Буквы/токены в домене</option>
|
|
<option value="list">Готовый список host</option>
|
|
</select>
|
|
</label>
|
|
|
|
<label class="templateBlock">
|
|
Шаблон host
|
|
<input id="template" type="text" placeholder="mirror{n}.example.com">
|
|
<small>Для цифр используй <code>{n}</code>, для букв/токенов — <code>{s}</code>. Примеры: <code>mirror{n}.example.com</code>, <code>{n}.example.com</code>, <code>node-{s}.example.com</code>.</small>
|
|
</label>
|
|
|
|
<fieldset id="numberBlock">
|
|
<legend>Цифровой перебор</legend>
|
|
<div class="row">
|
|
<label>От <input id="numberStart" type="number" min="0" step="1"></label>
|
|
<label>До <input id="numberEnd" type="number" min="0" step="1"></label>
|
|
<label>Шаг <input id="numberStep" type="number" min="1" step="1"></label>
|
|
<label>Нули слева <input id="padLength" type="number" min="0" step="1"></label>
|
|
</div>
|
|
<label class="checkline">
|
|
<input id="includeEmpty" type="checkbox">
|
|
<span>Пробовать вариант без цифры: <code id="includeEmptyHostExample">example.com</code> для шаблона <code id="includeEmptyTemplateExample">example{n}.com</code></span>
|
|
</label>
|
|
</fieldset>
|
|
|
|
<fieldset id="lettersBlock">
|
|
<legend>Буквенный/токенный перебор</legend>
|
|
<label>
|
|
Набор символов
|
|
<input id="letterCharset" type="text" placeholder="abcdefghijklmnopqrstuvwxyz">
|
|
</label>
|
|
<div class="row">
|
|
<label>Мин. длина <input id="letterMinLen" type="number" min="1" step="1"></label>
|
|
<label>Макс. длина <input id="letterMaxLen" type="number" min="1" step="1"></label>
|
|
<label>Лимит вариантов <input id="maxCandidates" type="number" min="1" step="1"></label>
|
|
</div>
|
|
</fieldset>
|
|
|
|
<fieldset id="listBlock">
|
|
<legend>Список host</legend>
|
|
<label>
|
|
Host-ы через новую строку, пробел или запятую
|
|
<textarea id="customHosts" rows="7" placeholder="mirror1.example.com mirror2.example.com"></textarea>
|
|
</label>
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend>Тайминги и защита от циклов</legend>
|
|
<div class="row">
|
|
<label>Timeout проверки, мс <input id="timeoutMs" type="number" min="500" step="100"></label>
|
|
<label>Параллельных проверок <input id="parallelChecks" type="number" min="1" step="1"></label>
|
|
<label>Макс. редиректов <input id="maxRedirects" type="number" min="1" step="1"></label>
|
|
<label>Кэш рабочего host, часов <input id="cacheTtlHours" type="number" min="0" step="1"><small>0 = не забывать по времени; кэш сбрасывается только если host умер.</small></label>
|
|
</div>
|
|
<label class="checkline">
|
|
<input id="preferCachedHost" type="checkbox">
|
|
<span>Сразу открывать последний рабочий host, если он уже известен. Например, с <code id="cachedFromExample">example1.com/page.html</code> сразу перейти на <code id="cachedToExample">example2.com/page.html</code>.</span>
|
|
</label>
|
|
</fieldset>
|
|
|
|
<label>
|
|
Маркеры страницы блокировки/ошибки
|
|
<textarea id="blockedMarkers" rows="8"></textarea>
|
|
<small>Если загруженная страница содержит одну из этих строк, расширение считает зеркало нерабочим и переключается дальше.</small>
|
|
</label>
|
|
|
|
<div class="row row-actions">
|
|
<button id="applyRuleBtn" type="submit" class="primary">Применить к правилу</button>
|
|
<button id="duplicateRuleBtn" type="button">Дублировать</button>
|
|
<button id="clearRuleCacheBtn" type="button">Очистить кэш правила</button>
|
|
<button id="deleteRuleBtn" type="button" class="danger">Удалить</button>
|
|
</div>
|
|
</form>
|
|
</section>
|
|
</section>
|
|
|
|
<details class="card log-card" id="debugLogSection">
|
|
<summary class="section-summary log-summary">
|
|
<span class="log-summary-main">
|
|
<span class="log-title">Журнал диагностики</span>
|
|
<span class="log-description">Если расширение ведёт не туда, обнови журнал и экспортируй JSON или текст из окна.</span>
|
|
</span>
|
|
<span class="guide-badge">Хранится до 1200 последних событий</span>
|
|
</summary>
|
|
<div class="log-body">
|
|
<div class="row row-actions">
|
|
<button id="refreshLogBtn" type="button">Обновить журнал</button>
|
|
<button id="copyLogBtn" type="button">Копировать журнал</button>
|
|
<button id="exportLogBtn" type="button">Экспорт журнала</button>
|
|
<button id="clearLogBtn" type="button" class="danger">Очистить журнал</button>
|
|
</div>
|
|
<textarea id="debugLogOutput" class="debug-log" rows="12" readonly placeholder="Здесь появятся события редиректов, кэша, Cloudflare-маркеров и выбора кандидатов."></textarea>
|
|
<small>В журнале могут быть URL страниц, которые ты открывал через правила расширения. Перед публикацией проверь, нет ли там лишнего.</small>
|
|
</div>
|
|
</details>
|
|
</main>
|
|
|
|
<div id="toastContainer" class="toast-container" aria-live="polite" aria-atomic="false"></div>
|
|
|
|
<script src="shared.js"></script>
|
|
<script src="i18n.js"></script>
|
|
<script src="options.js"></script>
|
|
</body>
|
|
</html>
|