Web UI: без полоски у кнопки StressKVN, Steer с заглавной
Градиент кнопки повторялся под прозрачной рамкой, и слева виднелся его противоположный конец. Фон теперь начинается от края рамки и не повторяется. Название движка в текстах интерфейса — Steer.
This commit is contained in:
1 parent
7c6410d587
commit
61bff13932
1 file changed
+58
-57
+58
-57
@@ -12,7 +12,7 @@ echo -e "\n${MAGENTA}Устанавливаем Zapret Manager для LuCI${NC}"
|
|||||||
# этом меняет стратегии zapret, и обрывать его посередине значит оставить роутер на случайной.
|
# этом меняет стратегии zapret, и обрывать его посередине значит оставить роутер на случайной.
|
||||||
for _zm_job in redbtn steer; do
|
for _zm_job in redbtn steer; do
|
||||||
if [ -f /tmp/zapret-manager-luci/$_zm_job.pid ] && kill -0 "$(cat /tmp/zapret-manager-luci/$_zm_job.pid 2>/dev/null)" 2>/dev/null; then
|
if [ -f /tmp/zapret-manager-luci/$_zm_job.pid ] && kill -0 "$(cat /tmp/zapret-manager-luci/$_zm_job.pid 2>/dev/null)" 2>/dev/null; then
|
||||||
echo -e "${YELLOW}Идёт автообход или операция steer — дождитесь окончания и запустите установку снова${NC}"
|
echo -e "${YELLOW}Идёт автообход или операция Steer — дождитесь окончания и запустите установку снова${NC}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
@@ -4886,14 +4886,14 @@ _st_steer_ver() { steer --version 2>/dev/null | head -n1 | awk '{print $2}'; }
|
|||||||
|
|
||||||
_st_install_steer() {
|
_st_install_steer() {
|
||||||
if command -v steer >/dev/null 2>&1; then
|
if command -v steer >/dev/null 2>&1; then
|
||||||
_rb_say "Движок steer уже установлен: $(_st_steer_ver)"
|
_rb_say "Движок Steer уже установлен: $(_st_steer_ver)"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
local arch tmp base url ver
|
local arch tmp base url ver
|
||||||
arch="$(_rb_arch)"
|
arch="$(_rb_arch)"
|
||||||
[ -n "$arch" ] || { echo "ОШИБКА: не удалось определить архитектуру роутера"; return 1; }
|
[ -n "$arch" ] || { echo "ОШИБКА: не удалось определить архитектуру роутера"; return 1; }
|
||||||
tmp="$ST_RUN/steer.$RAZ"
|
tmp="$ST_RUN/steer.$RAZ"
|
||||||
_rb_say "Устанавливаем движок steer $ST_STEER_VER"
|
_rb_say "Устанавливаем движок Steer $ST_STEER_VER"
|
||||||
$UPDATE >/dev/null 2>&1
|
$UPDATE >/dev/null 2>&1
|
||||||
for base in $ST_STEER_URLS; do
|
for base in $ST_STEER_URLS; do
|
||||||
ver="$ST_STEER_VER"
|
ver="$ST_STEER_VER"
|
||||||
@@ -4913,12 +4913,12 @@ _st_install_steer() {
|
|||||||
/etc/init.d/steer stop >/dev/null 2>&1
|
/etc/init.d/steer stop >/dev/null 2>&1
|
||||||
/etc/init.d/steer disable >/dev/null 2>&1
|
/etc/init.d/steer disable >/dev/null 2>&1
|
||||||
_rb_rpcd_ensure
|
_rb_rpcd_ensure
|
||||||
_rb_say "Движок steer $ver установлен"
|
_rb_say "Движок Steer $ver установлен"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
rm -f "$tmp"
|
rm -f "$tmp"
|
||||||
echo "ОШИБКА: не удалось установить движок steer"
|
echo "ОШИБКА: не удалось установить движок Steer"
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5487,7 +5487,7 @@ _st_spec_apply() { # ID...
|
|||||||
local tmp="$ST_RUN/spec.json" out
|
local tmp="$ST_RUN/spec.json" out
|
||||||
_st_spec_build "$@" > "$tmp" || { echo "ОШИБКА: для выбранных сервисов нет ни одного списка"; return 1; }
|
_st_spec_build "$@" > "$tmp" || { echo "ОШИБКА: для выбранных сервисов нет ни одного списка"; return 1; }
|
||||||
if ! out=$(steer apply --spec "$tmp" --dry-run 2>&1 >/dev/null); then
|
if ! out=$(steer apply --spec "$tmp" --dry-run 2>&1 >/dev/null); then
|
||||||
echo "ОШИБКА: движок steer отверг настройку"
|
echo "ОШИБКА: движок Steer отверг настройку"
|
||||||
printf '%s\n' "$out" | tail -n 5
|
printf '%s\n' "$out" | tail -n 5
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
@@ -5496,14 +5496,14 @@ _st_spec_apply() { # ID...
|
|||||||
cp "$ST_STEER_SPEC" "$ST_DIR/spec.before"
|
cp "$ST_STEER_SPEC" "$ST_DIR/spec.before"
|
||||||
fi
|
fi
|
||||||
if [ -s "$ST_STEER_SPEC" ] && cmp -s "$tmp" "$ST_STEER_SPEC" && /etc/init.d/steer running >/dev/null 2>&1; then
|
if [ -s "$ST_STEER_SPEC" ] && cmp -s "$tmp" "$ST_STEER_SPEC" && /etc/init.d/steer running >/dev/null 2>&1; then
|
||||||
_rb_say "Правила steer не изменились"
|
_rb_say "Правила Steer не изменились"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
cp "$tmp" "$ST_STEER_SPEC.tmp" && mv "$ST_STEER_SPEC.tmp" "$ST_STEER_SPEC"
|
cp "$tmp" "$ST_STEER_SPEC.tmp" && mv "$ST_STEER_SPEC.tmp" "$ST_STEER_SPEC"
|
||||||
_st_own "steer-spec"
|
_st_own "steer-spec"
|
||||||
/etc/init.d/steer enable >/dev/null 2>&1
|
/etc/init.d/steer enable >/dev/null 2>&1
|
||||||
/etc/init.d/steer restart >/dev/null 2>&1
|
/etc/init.d/steer restart >/dev/null 2>&1
|
||||||
_rb_say "Правила steer применены"
|
_rb_say "Правила Steer применены"
|
||||||
}
|
}
|
||||||
|
|
||||||
_st_spec_clear() {
|
_st_spec_clear() {
|
||||||
@@ -5525,7 +5525,7 @@ _st_kick() {
|
|||||||
sel="$(_st_sel | tr '\n' ' ')"
|
sel="$(_st_sel | tr '\n' ' ')"
|
||||||
if [ -n "$(echo $sel)" ] && _st_spec_apply $sel; then return 0; fi
|
if [ -n "$(echo $sel)" ] && _st_spec_apply $sel; then return 0; fi
|
||||||
/etc/init.d/steer restart >/dev/null 2>&1
|
/etc/init.d/steer restart >/dev/null 2>&1
|
||||||
_rb_say "steer перезапущен"
|
_rb_say "Steer перезапущен"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -5635,7 +5635,7 @@ _st_apply() { # [tunnel_ready] — туннель только что прове
|
|||||||
if [ -z "$(echo $sel)" ]; then
|
if [ -z "$(echo $sel)" ]; then
|
||||||
_st_spec_clear
|
_st_spec_clear
|
||||||
_st_down
|
_st_down
|
||||||
_rb_say "Ничего не выбрано — туннель и steer выключены"
|
_rb_say "Ничего не выбрано — туннель и Steer выключены"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
[ "$1" = tunnel_ready ] || _st_warp_up || return 1
|
[ "$1" = tunnel_ready ] || _st_warp_up || return 1
|
||||||
@@ -5661,7 +5661,7 @@ _st_selfcheck() {
|
|||||||
*warp=on*|*warp=plus*) echo "[ OK ] Туннель WARP: трафик идёт через Cloudflare ($(echo "$trace" | sed -n 's/^colo=//p'))" ;;
|
*warp=on*|*warp=plus*) echo "[ OK ] Туннель WARP: трафик идёт через Cloudflare ($(echo "$trace" | sed -n 's/^colo=//p'))" ;;
|
||||||
*) echo "[FAIL] Туннель WARP: трафик через него не идёт"; bad=1 ;;
|
*) echo "[FAIL] Туннель WARP: трафик через него не идёт"; bad=1 ;;
|
||||||
esac
|
esac
|
||||||
/etc/init.d/steer running >/dev/null 2>&1 && echo "[ OK ] Служба steer запущена" || { echo "[FAIL] Служба steer не запущена"; bad=1; }
|
/etc/init.d/steer running >/dev/null 2>&1 && echo "[ OK ] Служба Steer запущена" || { echo "[FAIL] Служба Steer не запущена"; bad=1; }
|
||||||
fi
|
fi
|
||||||
command -v steer >/dev/null 2>&1 || return $bad
|
command -v steer >/dev/null 2>&1 || return $bad
|
||||||
[ -s "$ST_STEER_SPEC" ] || return $bad
|
[ -s "$ST_STEER_SPEC" ] || return $bad
|
||||||
@@ -5699,7 +5699,7 @@ do_steer_install() {
|
|||||||
blk="$(_st_blocker)"
|
blk="$(_st_blocker)"
|
||||||
case "$blk" in
|
case "$blk" in
|
||||||
splify2) echo "ОШИБКА: установлен splify2 — туннели и списки настраиваются в нём"; return 1 ;;
|
splify2) echo "ОШИБКА: установлен splify2 — туннели и списки настраиваются в нём"; return 1 ;;
|
||||||
steer) echo "ОШИБКА: у движка steer уже есть чужие правила — не перезаписываем их"; return 1 ;;
|
steer) echo "ОШИБКА: у движка Steer уже есть чужие правила — не перезаписываем их"; return 1 ;;
|
||||||
esac
|
esac
|
||||||
_ensure_deps
|
_ensure_deps
|
||||||
mkdir -p "$ST_DIR"
|
mkdir -p "$ST_DIR"
|
||||||
@@ -5728,14 +5728,14 @@ do_steer_install() {
|
|||||||
_st_phase check
|
_st_phase check
|
||||||
sleep 2
|
sleep 2
|
||||||
_st_selfcheck
|
_st_selfcheck
|
||||||
_rb_say "Готово, steer установлен и работает"
|
_rb_say "Готово, Steer установлен и работает"
|
||||||
}
|
}
|
||||||
|
|
||||||
do_steer_apply() {
|
do_steer_apply() {
|
||||||
_st_phase rules
|
_st_phase rules
|
||||||
rm -f "$ST_STOP_FLAG"
|
rm -f "$ST_STOP_FLAG"
|
||||||
_st_installed || { echo "ОШИБКА: steer ещё не установлен"; return 1; }
|
_st_installed || { echo "ОШИБКА: Steer ещё не установлен"; return 1; }
|
||||||
[ -n "$(_st_blocker)" ] && { echo "ОШИБКА: спеку steer сейчас ведёт не Zapret Manager"; return 1; }
|
[ -n "$(_st_blocker)" ] && { echo "ОШИБКА: спеку Steer сейчас ведёт не Zapret Manager"; return 1; }
|
||||||
rm -f "$ST_OFF"
|
rm -f "$ST_OFF"
|
||||||
_st_apply || return 1
|
_st_apply || return 1
|
||||||
_st_phase check
|
_st_phase check
|
||||||
@@ -5746,16 +5746,16 @@ do_steer_apply() {
|
|||||||
|
|
||||||
do_steer_stop() {
|
do_steer_stop() {
|
||||||
_st_phase rules
|
_st_phase rules
|
||||||
_st_installed || { echo "ОШИБКА: steer ещё не установлен"; return 1; }
|
_st_installed || { echo "ОШИБКА: Steer ещё не установлен"; return 1; }
|
||||||
touch "$ST_OFF"
|
touch "$ST_OFF"
|
||||||
_st_down
|
_st_down
|
||||||
_rb_say "Готово, steer и туннель выключены — всё идёт напрямую"
|
_rb_say "Готово, Steer и туннель выключены — всё идёт напрямую"
|
||||||
}
|
}
|
||||||
|
|
||||||
_st_need_warp() {
|
_st_need_warp() {
|
||||||
[ -n "$(_st_blocker)" ] && { echo "ОШИБКА: движок steer сейчас настраивает не Zapret Manager"; return 1; }
|
[ -n "$(_st_blocker)" ] && { echo "ОШИБКА: движок Steer сейчас настраивает не Zapret Manager"; return 1; }
|
||||||
_st_owns "net $ST_WARP_IF" && [ -s "$ST_WARP_CONF" ] && return 0
|
_st_owns "net $ST_WARP_IF" && [ -s "$ST_WARP_CONF" ] && return 0
|
||||||
echo "ОШИБКА: туннеля WARP ещё нет — сначала установите steer"
|
echo "ОШИБКА: туннеля WARP ещё нет — сначала установите Steer"
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5809,14 +5809,14 @@ do_steer_warp_recreate() {
|
|||||||
|
|
||||||
do_steer_remove() {
|
do_steer_remove() {
|
||||||
_st_phase remove
|
_st_phase remove
|
||||||
_rb_say "Удаляем steer и туннель WARP"
|
_rb_say "Удаляем Steer и туннель WARP"
|
||||||
# Встал splify2 — движок и спека теперь его: их не трогаем, снимаем только своё (туннели,
|
# Встал splify2 — движок и спека теперь его: их не трогаем, снимаем только своё (туннели,
|
||||||
# зону, cron).
|
# зону, cron).
|
||||||
local foreign=0
|
local foreign=0
|
||||||
[ "$(_st_blocker)" = splify2 ] && foreign=1
|
[ "$(_st_blocker)" = splify2 ] && foreign=1
|
||||||
if [ "$foreign" = 1 ]; then
|
if [ "$foreign" = 1 ]; then
|
||||||
sed -i '/^steer-spec$/d; /^pkg steer$/d' "$ST_OWNED" 2>/dev/null
|
sed -i '/^steer-spec$/d; /^pkg steer$/d' "$ST_OWNED" 2>/dev/null
|
||||||
_rb_warn "Установлен splify2 — движок steer и его правила оставляем ему"
|
_rb_warn "Установлен splify2 — движок Steer и его правила оставляем ему"
|
||||||
else
|
else
|
||||||
_st_spec_clear
|
_st_spec_clear
|
||||||
/etc/init.d/steer stop >/dev/null 2>&1
|
/etc/init.d/steer stop >/dev/null 2>&1
|
||||||
@@ -5841,7 +5841,7 @@ do_steer_remove() {
|
|||||||
/etc/init.d/cron restart >/dev/null 2>&1
|
/etc/init.d/cron restart >/dev/null 2>&1
|
||||||
fi
|
fi
|
||||||
if _st_owns "pkg steer"; then
|
if _st_owns "pkg steer"; then
|
||||||
_rb_say "Удаляем движок steer"
|
_rb_say "Удаляем движок Steer"
|
||||||
$DELETE steer >/dev/null 2>&1
|
$DELETE steer >/dev/null 2>&1
|
||||||
fi
|
fi
|
||||||
_st_owns "pkg conntrack" && $DELETE conntrack >/dev/null 2>&1
|
_st_owns "pkg conntrack" && $DELETE conntrack >/dev/null 2>&1
|
||||||
@@ -5856,7 +5856,7 @@ do_steer_remove() {
|
|||||||
sed -i 's/|warp$/|none/' /etc/zm-redbtn/services 2>/dev/null
|
sed -i 's/|warp$/|none/' /etc/zm-redbtn/services 2>/dev/null
|
||||||
_rb_rpcd_ensure
|
_rb_rpcd_ensure
|
||||||
rm -rf "$ST_DIR" "$ST_RUN"
|
rm -rf "$ST_DIR" "$ST_RUN"
|
||||||
_rb_say "Готово, steer удалён"
|
_rb_say "Готово, Steer удалён"
|
||||||
}
|
}
|
||||||
|
|
||||||
steer_status() {
|
steer_status() {
|
||||||
@@ -5983,11 +5983,11 @@ steer_action() {
|
|||||||
;;
|
;;
|
||||||
restart)
|
restart)
|
||||||
_st_running && { echo '{"error":"дождитесь окончания текущей операции"}'; return 1; }
|
_st_running && { echo '{"error":"дождитесь окончания текущей операции"}'; return 1; }
|
||||||
_st_owns "steer-spec" || { echo '{"error":"правил для steer нет — выберите сервисы"}'; return 1; }
|
_st_owns "steer-spec" || { echo '{"error":"правил для Steer нет — выберите сервисы"}'; return 1; }
|
||||||
/etc/init.d/steer enable >/dev/null 2>&1
|
/etc/init.d/steer enable >/dev/null 2>&1
|
||||||
/etc/init.d/steer restart >/dev/null 2>&1
|
/etc/init.d/steer restart >/dev/null 2>&1
|
||||||
sleep 1
|
sleep 1
|
||||||
/etc/init.d/steer running >/dev/null 2>&1 || { echo '{"error":"steer не запустился — загляните в системный журнал"}'; return 1; }
|
/etc/init.d/steer running >/dev/null 2>&1 || { echo '{"error":"Steer не запустился — загляните в системный журнал"}'; return 1; }
|
||||||
printf '{"ok":true}\n'
|
printf '{"ok":true}\n'
|
||||||
;;
|
;;
|
||||||
autorestart) _st_cron_set "$mode" ;;
|
autorestart) _st_cron_set "$mode" ;;
|
||||||
@@ -6688,7 +6688,7 @@ do_redbtn_run() {
|
|||||||
zapret2) echo "ОШИБКА: установлен Zapret2 — сначала удалите его"; return 1 ;;
|
zapret2) echo "ОШИБКА: установлен Zapret2 — сначала удалите его"; return 1 ;;
|
||||||
esac
|
esac
|
||||||
_rb_test_running && { echo "ОШИБКА: идёт тест стратегий — дождитесь его окончания"; return 1; }
|
_rb_test_running && { echo "ОШИБКА: идёт тест стратегий — дождитесь его окончания"; return 1; }
|
||||||
_job_alive steer && { echo "ОШИБКА: на странице steer идёт операция — дождитесь её окончания"; return 1; }
|
_job_alive steer && { echo "ОШИБКА: на странице Steer идёт операция — дождитесь её окончания"; return 1; }
|
||||||
: > "$RB_RESULTS"
|
: > "$RB_RESULTS"
|
||||||
|
|
||||||
_rb_phase zapret_install
|
_rb_phase zapret_install
|
||||||
@@ -6799,7 +6799,7 @@ do_redbtn_run() {
|
|||||||
if ! _rb_routable "$id"; then
|
if ! _rb_routable "$id"; then
|
||||||
_rb_result_write "$id" none
|
_rb_result_write "$id" none
|
||||||
elif _rb_in "$id" "$ST_SKIP"; then
|
elif _rb_in "$id" "$ST_SKIP"; then
|
||||||
echo "[ -- ] $(_rb_svc_field "$id" 2) — через WARP не пускаем: вы убрали его на странице steer"
|
echo "[ -- ] $(_rb_svc_field "$id" 2) — через WARP не пускаем: вы убрали его на странице Steer"
|
||||||
_rb_result_write "$id" none
|
_rb_result_write "$id" none
|
||||||
elif _rb_check_service "$id" warp; then
|
elif _rb_check_service "$id" warp; then
|
||||||
echo "[ OK ] $(_rb_svc_field "$id" 2) — через WARP"
|
echo "[ OK ] $(_rb_svc_field "$id" 2) — через WARP"
|
||||||
@@ -6814,14 +6814,14 @@ do_redbtn_run() {
|
|||||||
if [ -n "$warp_ids" ]; then
|
if [ -n "$warp_ids" ]; then
|
||||||
_rb_phase steer
|
_rb_phase steer
|
||||||
# Туннели только что подняты и проверены — второй раз не поднимаем.
|
# Туннели только что подняты и проверены — второй раз не поднимаем.
|
||||||
_st_apply tunnel_ready || _rb_warn "steer не принял правила — загляните на страницу steer"
|
_st_apply tunnel_ready || _rb_warn "Steer не принял правила — загляните на страницу Steer"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
for id in $failed; do _rb_result_write "$id" none; done
|
for id in $failed; do _rb_result_write "$id" none; done
|
||||||
fi
|
fi
|
||||||
elif [ -n "$failed" ]; then
|
elif [ -n "$failed" ]; then
|
||||||
for id in $failed; do _rb_result_write "$id" none; done
|
for id in $failed; do _rb_result_write "$id" none; done
|
||||||
_rb_warn "Не открылось: $(_rb_svc_names "$failed"). Их можно пустить через WARP — установите steer на странице «steer»"
|
_rb_warn "Не открылось: $(_rb_svc_names "$failed"). Их можно пустить через WARP — установите Steer на странице «Steer»"
|
||||||
fi
|
fi
|
||||||
_rb_stopped && { _rb_say "Остановлено"; return 0; }
|
_rb_stopped && { _rb_say "Остановлено"; return 0; }
|
||||||
|
|
||||||
@@ -6834,7 +6834,7 @@ do_redbtn_run() {
|
|||||||
if _st_ready && _rb_in telegram "$ST_SEL"; then
|
if _st_ready && _rb_in telegram "$ST_SEL"; then
|
||||||
# Telegram отмечен на странице steer — он уже идёт через WARP, веб-сокет не нужен.
|
# Telegram отмечен на странице steer — он уже идёт через WARP, веб-сокет не нужен.
|
||||||
_rb_result_write telegram warp
|
_rb_result_write telegram warp
|
||||||
echo "[ OK ] Telegram — через WARP (выбран на странице steer)"
|
echo "[ OK ] Telegram — через WARP (выбран на странице Steer)"
|
||||||
elif [ -x /etc/init.d/tgws ] && [ -n "$(tgws status 2>/dev/null)" ]; then
|
elif [ -x /etc/init.d/tgws ] && [ -n "$(tgws status 2>/dev/null)" ]; then
|
||||||
_rb_result_write telegram tgws
|
_rb_result_write telegram tgws
|
||||||
echo "[ OK ] Telegram — через веб-сокет"
|
echo "[ OK ] Telegram — через веб-сокет"
|
||||||
@@ -6856,7 +6856,7 @@ do_redbtn_run() {
|
|||||||
_rb_phase done
|
_rb_phase done
|
||||||
date +%s > "$RB_LAST"
|
date +%s > "$RB_LAST"
|
||||||
if [ -n "$warp_ids" ] && _st_dns_conflict; then
|
if [ -n "$warp_ids" ] && _st_dns_conflict; then
|
||||||
_rb_warn "DNS over HTTPS перехватывает DNS сети — сервисы через WARP работать не будут, нажмите «Исправить» на странице steer"
|
_rb_warn "DNS over HTTPS перехватывает DNS сети — сервисы через WARP работать не будут, нажмите «Исправить» на странице Steer"
|
||||||
fi
|
fi
|
||||||
_rb_say "Готово, обход подобран"
|
_rb_say "Готово, обход подобран"
|
||||||
}
|
}
|
||||||
@@ -6935,7 +6935,7 @@ redbtn_action() {
|
|||||||
start)
|
start)
|
||||||
case "$mode" in quick|full) ;; *) mode=quick ;; esac
|
case "$mode" in quick|full) ;; *) mode=quick ;; esac
|
||||||
_rb_test_running && { echo '{"error":"идёт тест стратегий — дождитесь его окончания"}'; return 1; }
|
_rb_test_running && { echo '{"error":"идёт тест стратегий — дождитесь его окончания"}'; return 1; }
|
||||||
_job_alive steer && { echo '{"error":"на странице steer идёт операция — дождитесь её окончания"}'; return 1; }
|
_job_alive steer && { echo '{"error":"на странице Steer идёт операция — дождитесь её окончания"}'; return 1; }
|
||||||
job_start redbtn do_redbtn_run "$mode"
|
job_start redbtn do_redbtn_run "$mode"
|
||||||
;;
|
;;
|
||||||
stop)
|
stop)
|
||||||
@@ -7302,7 +7302,7 @@ cat > '/usr/share/luci/menu.d/luci-app-zapret-manager.json' << 'ZM_INSTALLER_EOF
|
|||||||
"action": { "type": "view", "path": "zapret-manager/redbtn" }
|
"action": { "type": "view", "path": "zapret-manager/redbtn" }
|
||||||
},
|
},
|
||||||
"admin/services/zapret-manager/steer": {
|
"admin/services/zapret-manager/steer": {
|
||||||
"title": "steer",
|
"title": "Steer",
|
||||||
"order": 16,
|
"order": 16,
|
||||||
"action": { "type": "view", "path": "zapret-manager/steer" }
|
"action": { "type": "view", "path": "zapret-manager/steer" }
|
||||||
},
|
},
|
||||||
@@ -7781,7 +7781,7 @@ return view.extend({
|
|||||||
items.push(row('Автообход', rbSt === 1 ? zm.badge(true, 'настроен', '')
|
items.push(row('Автообход', rbSt === 1 ? zm.badge(true, 'настроен', '')
|
||||||
: E('span', { 'class': 'zm-badge ' + (rbSt === 2 ? 'zm-warn' : 'zm-off') }, [ E('span', { 'class': 'zm-dot' }), rbSt === 2 ? 'требует внимания' : 'не запускался' ])));
|
: E('span', { 'class': 'zm-badge ' + (rbSt === 2 ? 'zm-warn' : 'zm-off') }, [ E('span', { 'class': 'zm-dot' }), rbSt === 2 ? 'требует внимания' : 'не запускался' ])));
|
||||||
var stSt = st(h, 'steer', 0);
|
var stSt = st(h, 'steer', 0);
|
||||||
items.push(row('steer (WARP)', stSt === 1 ? zm.badge(true, 'работает', '') : stSt === 2
|
items.push(row('Steer (WARP)', stSt === 1 ? zm.badge(true, 'работает', '') : stSt === 2
|
||||||
? E('span', { 'class': 'zm-badge zm-warn' }, [ E('span', { 'class': 'zm-dot' }), 'выключен' ]) : zm.badge(false, '', 'не установлен')));
|
? E('span', { 'class': 'zm-badge zm-warn' }, [ E('span', { 'class': 'zm-dot' }), 'выключен' ]) : zm.badge(false, '', 'не установлен')));
|
||||||
items.push(row('ByeTube', zm.stateBadge(st(h, 'bytetube', 0))));
|
items.push(row('ByeTube', zm.stateBadge(st(h, 'bytetube', 0))));
|
||||||
items.push(row('TG WS Proxy', zm.stateBadge(st(h, 'tg', 0))));
|
items.push(row('TG WS Proxy', zm.stateBadge(st(h, 'tg', 0))));
|
||||||
@@ -8371,7 +8371,7 @@ return view.extend({
|
|||||||
E('div', { 'class': 'zm-ab-icon' }, [ E(ICON_BOLT) ]),
|
E('div', { 'class': 'zm-ab-icon' }, [ E(ICON_BOLT) ]),
|
||||||
E('div', { 'class': 'zm-ab-title' }, [
|
E('div', { 'class': 'zm-ab-title' }, [
|
||||||
E('h3', {}, 'Автообход'),
|
E('h3', {}, 'Автообход'),
|
||||||
E('p', { 'class': 'zm-hint' }, 'Одна кнопка — и роутер сам подберёт каждому сервису рабочий способ: напрямую или через Zapret, а если установлен steer — и через WARP.')
|
E('p', { 'class': 'zm-hint' }, 'Одна кнопка — и роутер сам подберёт каждому сервису рабочий способ: напрямую или через Zapret, а если установлен Steer — и через WARP.')
|
||||||
]),
|
]),
|
||||||
status
|
status
|
||||||
]));
|
]));
|
||||||
@@ -8380,7 +8380,7 @@ return view.extend({
|
|||||||
heroCard.appendChild(E('div', { 'class': 'zm-ab-note zm-ab-note-warn' }, BLOCKERS[data.blocker] || data.blocker));
|
heroCard.appendChild(E('div', { 'class': 'zm-ab-note zm-ab-note-warn' }, BLOCKERS[data.blocker] || data.blocker));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!busy && data.steer_busy) heroCard.appendChild(E('div', { 'class': 'zm-ab-note zm-ab-note-warn' }, 'На странице steer идёт операция — запуск станет доступен, когда она закончится.'));
|
if (!busy && data.steer_busy) heroCard.appendChild(E('div', { 'class': 'zm-ab-note zm-ab-note-warn' }, 'На странице Steer идёт операция — запуск станет доступен, когда она закончится.'));
|
||||||
|
|
||||||
if (busy) {
|
if (busy) {
|
||||||
if (RUN_PHASES.indexOf(data.phase) >= 0 || lastAction === 'start') heroCard.appendChild(renderSteps());
|
if (RUN_PHASES.indexOf(data.phase) >= 0 || lastAction === 'start') heroCard.appendChild(renderSteps());
|
||||||
@@ -8456,9 +8456,9 @@ return view.extend({
|
|||||||
steerCard.style.display = '';
|
steerCard.style.display = '';
|
||||||
steerCard.appendChild(E('div', { 'class': 'zm-card' }, [
|
steerCard.appendChild(E('div', { 'class': 'zm-card' }, [
|
||||||
E('h3', {}, 'Не всё открылось'),
|
E('h3', {}, 'Не всё открылось'),
|
||||||
E('p', { 'class': 'zm-hint' }, bad.map(function(s) { return s.name; }).join(', ') + ' — не помог и Zapret. Их можно пустить через бесплатный туннель WARP: установите steer, и они сразу пойдут через него.'),
|
E('p', { 'class': 'zm-hint' }, bad.map(function(s) { return s.name; }).join(', ') + ' — не помог и Zapret. Их можно пустить через бесплатный туннель WARP: установите Steer, и они сразу пойдут через него.'),
|
||||||
E('div', { 'class': 'zm-actions' }, [
|
E('div', { 'class': 'zm-actions' }, [
|
||||||
E('a', { 'class': 'cbi-button cbi-button-positive', 'style': 'text-decoration:none', 'href': steerHref() }, 'Открыть steer')
|
E('a', { 'class': 'cbi-button cbi-button-positive', 'style': 'text-decoration:none', 'href': steerHref() }, 'Открыть Steer')
|
||||||
])
|
])
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
@@ -8573,7 +8573,7 @@ cat > '/www/luci-static/resources/view/zapret-manager/steer.js' << 'ZM_INSTALLER
|
|||||||
// Страница ставит и связывает всё сама: движок steer, AmneziaWG, ключи WARP, туннель и правила.
|
// Страница ставит и связывает всё сама: движок steer, AmneziaWG, ключи WARP, туннель и правила.
|
||||||
|
|
||||||
var STEPS = [
|
var STEPS = [
|
||||||
{ id: 'pkgs', label: 'steer' },
|
{ id: 'pkgs', label: 'Steer' },
|
||||||
{ id: 'awg', label: 'AmneziaWG' },
|
{ id: 'awg', label: 'AmneziaWG' },
|
||||||
{ id: 'keys', label: 'Ключи WARP' },
|
{ id: 'keys', label: 'Ключи WARP' },
|
||||||
{ id: 'tunnel', label: 'Туннель' },
|
{ id: 'tunnel', label: 'Туннель' },
|
||||||
@@ -8582,14 +8582,14 @@ var STEPS = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
var PHASE_TEXT = {
|
var PHASE_TEXT = {
|
||||||
install: 'Устанавливаем', pkgs: 'Ставим steer', awg: 'Ставим AmneziaWG', keys: 'Получаем ключи WARP',
|
install: 'Устанавливаем', pkgs: 'Ставим Steer', awg: 'Ставим AmneziaWG', keys: 'Получаем ключи WARP',
|
||||||
tunnel: 'Поднимаем туннель', warp: 'Настраиваем туннель', rules: 'Применяем правила',
|
tunnel: 'Поднимаем туннель', warp: 'Настраиваем туннель', rules: 'Применяем правила',
|
||||||
check: 'Проверяем', remove: 'Удаляем'
|
check: 'Проверяем', remove: 'Удаляем'
|
||||||
};
|
};
|
||||||
|
|
||||||
var BLOCKERS = {
|
var BLOCKERS = {
|
||||||
splify2: 'Установлен splify2 — туннели и списки настраиваются в нём.',
|
splify2: 'Установлен splify2 — туннели и списки настраиваются в нём.',
|
||||||
steer: 'Движок steer уже настроен вручную — Zapret Manager его не перезаписывает.'
|
steer: 'Движок Steer уже настроен вручную — Zapret Manager его не перезаписывает.'
|
||||||
};
|
};
|
||||||
|
|
||||||
// Значок сервиса: буква на фирменном цвете.
|
// Значок сервиса: буква на фирменном цвете.
|
||||||
@@ -8662,9 +8662,9 @@ return view.extend({
|
|||||||
if (res) data = res;
|
if (res) data = res;
|
||||||
var msg = 'Готово';
|
var msg = 'Готово';
|
||||||
if (!ok) msg = 'Не получилось — подробности в журнале';
|
if (!ok) msg = 'Не получилось — подробности в журнале';
|
||||||
else if (lastAction === 'install') msg = 'steer установлен и работает';
|
else if (lastAction === 'install') msg = 'Steer установлен и работает';
|
||||||
else if (lastAction === 'remove') msg = 'steer удалён';
|
else if (lastAction === 'remove') msg = 'Steer удалён';
|
||||||
else if (lastAction === 'stop') msg = 'steer выключен — всё идёт напрямую';
|
else if (lastAction === 'stop') msg = 'Steer выключен — всё идёт напрямую';
|
||||||
else if (lastAction === 'lists' || lastAction === 'start') msg = 'Готово, выбор применён';
|
else if (lastAction === 'lists' || lastAction === 'start') msg = 'Готово, выбор применён';
|
||||||
zm.toast(msg, ok ? 'info' : 'error');
|
zm.toast(msg, ok ? 'info' : 'error');
|
||||||
var done = lastAction;
|
var done = lastAction;
|
||||||
@@ -8770,7 +8770,7 @@ return view.extend({
|
|||||||
heroCard.appendChild(E('div', { 'class': 'zm-ab-head' }, [
|
heroCard.appendChild(E('div', { 'class': 'zm-ab-head' }, [
|
||||||
E('div', { 'class': 'zm-ab-icon' }, [ E(ICON_ROUTE) ]),
|
E('div', { 'class': 'zm-ab-icon' }, [ E(ICON_ROUTE) ]),
|
||||||
E('div', { 'class': 'zm-ab-title' }, [
|
E('div', { 'class': 'zm-ab-title' }, [
|
||||||
E('h3', {}, 'steer'),
|
E('h3', {}, 'Steer'),
|
||||||
E('p', { 'class': 'zm-hint' }, 'Выбранные сервисы идут через бесплатный туннель Cloudflare WARP. Остальной интернет — как обычно.')
|
E('p', { 'class': 'zm-hint' }, 'Выбранные сервисы идут через бесплатный туннель Cloudflare WARP. Остальной интернет — как обычно.')
|
||||||
]),
|
]),
|
||||||
status
|
status
|
||||||
@@ -8798,12 +8798,12 @@ return view.extend({
|
|||||||
if (!data.installed) {
|
if (!data.installed) {
|
||||||
heroCard.appendChild(E('div', { 'class': 'zm-st-promo' }, [
|
heroCard.appendChild(E('div', { 'class': 'zm-st-promo' }, [
|
||||||
E('div', {}, [ E('b', {}, '1 кнопка'), E('span', {}, 'всё ставится и настраивается само') ]),
|
E('div', {}, [ E('b', {}, '1 кнопка'), E('span', {}, 'всё ставится и настраивается само') ]),
|
||||||
E('div', {}, [ E('b', {}, '2–4 минуты'), E('span', {}, 'steer, AmneziaWG, ключи WARP, туннель') ]),
|
E('div', {}, [ E('b', {}, '2–4 минуты'), E('span', {}, 'Steer, AmneziaWG, ключи WARP, туннель') ]),
|
||||||
E('div', {}, [ E('b', {}, 'Только выбранное'), E('span', {}, 'остальной трафик не трогается') ])
|
E('div', {}, [ E('b', {}, 'Только выбранное'), E('span', {}, 'остальной трафик не трогается') ])
|
||||||
]));
|
]));
|
||||||
heroCard.appendChild(E('div', { 'class': 'zm-actions' }, [
|
heroCard.appendChild(E('div', { 'class': 'zm-actions' }, [
|
||||||
E('button', { 'class': 'cbi-button cbi-button-positive zm-ab-go', 'click': function() {
|
E('button', { 'class': 'cbi-button cbi-button-positive zm-ab-go', 'click': function() {
|
||||||
act('install', '', 'Устанавливаем steer — это займёт пару минут');
|
act('install', '', 'Устанавливаем Steer — это займёт пару минут');
|
||||||
} }, [ E(ICON_ROUTE), 'Установить и включить' ])
|
} }, [ E(ICON_ROUTE), 'Установить и включить' ])
|
||||||
]));
|
]));
|
||||||
heroCard.appendChild(E('p', { 'class': 'zm-hint' }, 'Сразу через WARP пойдут сервисы, отмеченные ниже. Выбор можно поменять в любой момент.'));
|
heroCard.appendChild(E('p', { 'class': 'zm-hint' }, 'Сразу через WARP пойдут сервисы, отмеченные ниже. Выбор можно поменять в любой момент.'));
|
||||||
@@ -8819,18 +8819,18 @@ return view.extend({
|
|||||||
]));
|
]));
|
||||||
|
|
||||||
var actions = [];
|
var actions = [];
|
||||||
if (data.stopped) actions.push(E('button', { 'class': 'cbi-button cbi-button-positive', 'click': function() { act('start', '', 'Включаем steer'); } }, 'Включить'));
|
if (data.stopped) actions.push(E('button', { 'class': 'cbi-button cbi-button-positive', 'click': function() { act('start', '', 'Включаем Steer'); } }, 'Включить'));
|
||||||
else {
|
else {
|
||||||
actions.push(E('button', { 'class': 'cbi-button cbi-button-positive', 'click': function() { act('apply', '', 'Перезапускаем туннель и правила'); } }, 'Перезапустить'));
|
actions.push(E('button', { 'class': 'cbi-button cbi-button-positive', 'click': function() { act('apply', '', 'Перезапускаем туннель и правила'); } }, 'Перезапустить'));
|
||||||
actions.push(E('button', { 'class': 'cbi-button', 'click': function() { act('stop', '', 'Выключаем steer'); } }, 'Выключить'));
|
actions.push(E('button', { 'class': 'cbi-button', 'click': function() { act('stop', '', 'Выключаем Steer'); } }, 'Выключить'));
|
||||||
}
|
}
|
||||||
actions.push(E('button', { 'class': 'cbi-button cbi-button-remove', 'click': function() {
|
actions.push(E('button', { 'class': 'cbi-button cbi-button-remove', 'click': function() {
|
||||||
if (!confirm('Удалить steer?\n\nБудут удалены steer, туннель WARP и всё, что для них ставилось. Сервисы, которые шли через WARP, пойдут напрямую.')) return;
|
if (!confirm('Удалить Steer?\n\nБудут удалены Steer, туннель WARP и всё, что для них ставилось. Сервисы, которые шли через WARP, пойдут напрямую.')) return;
|
||||||
act('remove', '', 'Удаляем steer');
|
act('remove', '', 'Удаляем Steer');
|
||||||
} }, 'Удалить'));
|
} }, 'Удалить'));
|
||||||
heroCard.appendChild(E('div', { 'class': 'zm-actions' }, actions));
|
heroCard.appendChild(E('div', { 'class': 'zm-actions' }, actions));
|
||||||
heroCard.appendChild(E('div', { 'class': 'zm-ab-foot' }, [
|
heroCard.appendChild(E('div', { 'class': 'zm-ab-foot' }, [
|
||||||
E('span', {}, 'Версия steer: ' + (data.version || '—')),
|
E('span', {}, 'Версия Steer: ' + (data.version || '—')),
|
||||||
E('span', {}, 'Выключить — всё пойдёт напрямую, настройки сохранятся.')
|
E('span', {}, 'Выключить — всё пойдёт напрямую, настройки сохранятся.')
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
@@ -8906,7 +8906,7 @@ return view.extend({
|
|||||||
checkCard.style.display = data.installed && !data.blocker ? '' : 'none';
|
checkCard.style.display = data.installed && !data.blocker ? '' : 'none';
|
||||||
if (!data.installed || data.blocker) return;
|
if (!data.installed || data.blocker) return;
|
||||||
checkCard.appendChild(E('h3', {}, 'Проверка'));
|
checkCard.appendChild(E('h3', {}, 'Проверка'));
|
||||||
checkCard.appendChild(E('p', { 'class': 'zm-hint' }, 'Роутер проверит туннель и спросит сам steer, всё ли на месте.'));
|
checkCard.appendChild(E('p', { 'class': 'zm-hint' }, 'Роутер проверит туннель и спросит сам Steer, всё ли на месте.'));
|
||||||
if (diagRes) {
|
if (diagRes) {
|
||||||
var items = [];
|
var items = [];
|
||||||
var tn = diagRes.tunnels || [];
|
var tn = diagRes.tunnels || [];
|
||||||
@@ -8948,7 +8948,7 @@ return view.extend({
|
|||||||
warpCard.appendChild(E('h3', {}, tl.length > 1 ? 'Туннели WARP' : 'Туннель WARP'));
|
warpCard.appendChild(E('h3', {}, tl.length > 1 ? 'Туннели WARP' : 'Туннель WARP'));
|
||||||
if (tl.length > 1) {
|
if (tl.length > 1) {
|
||||||
warpCard.appendChild(E('p', { 'class': 'zm-hint' },
|
warpCard.appendChild(E('p', { 'class': 'zm-hint' },
|
||||||
'Трафик идёт через самый быстрый живой туннель; упал один — steer сам переключится на другой.'));
|
'Трафик идёт через самый быстрый живой туннель; упал один — Steer сам переключится на другой.'));
|
||||||
warpCard.appendChild(E('div', { 'class': 'zm-st-tunnels' }, tl.map(function(t) {
|
warpCard.appendChild(E('div', { 'class': 'zm-st-tunnels' }, tl.map(function(t) {
|
||||||
var live = tunnelLive(t);
|
var live = tunnelLive(t);
|
||||||
return E('div', { 'class': 'zm-st-tunnel' }, [
|
return E('div', { 'class': 'zm-st-tunnel' }, [
|
||||||
@@ -8995,7 +8995,7 @@ return view.extend({
|
|||||||
autoCard.style.display = data.installed && !data.blocker ? '' : 'none';
|
autoCard.style.display = data.installed && !data.blocker ? '' : 'none';
|
||||||
if (!data.installed || data.blocker) return;
|
if (!data.installed || data.blocker) return;
|
||||||
autoCard.appendChild(E('h3', {}, 'Автоперезапуск'));
|
autoCard.appendChild(E('h3', {}, 'Автоперезапуск'));
|
||||||
autoCard.appendChild(E('p', { 'class': 'zm-hint' }, 'Туннель и steer перезапускаются по расписанию — помогает, если обход со временем «подвисает».'));
|
autoCard.appendChild(E('p', { 'class': 'zm-hint' }, 'Туннель и Steer перезапускаются по расписанию — помогает, если обход со временем «подвисает».'));
|
||||||
var cur = data.autorestart || '';
|
var cur = data.autorestart || '';
|
||||||
var am = cur === '' ? 'off' : (cur.indexOf('every:') === 0 ? 'every' + cur.split(':')[1] : 'daily');
|
var am = cur === '' ? 'off' : (cur.indexOf('every:') === 0 ? 'every' + cur.split(':')[1] : 'daily');
|
||||||
var curHour = am === 'daily' ? parseInt(cur.split(':')[1], 10) : 4;
|
var curHour = am === 'daily' ? parseInt(cur.split(':')[1], 10) : 4;
|
||||||
@@ -14889,7 +14889,7 @@ function toggleTheme() {
|
|||||||
var ROUTES = [
|
var ROUTES = [
|
||||||
{ id: 'dashboard', title: 'Дашборд', sub: 'Состояние всех компонентов', icon: 'dashboard', group: 'Обзор' },
|
{ id: 'dashboard', title: 'Дашборд', sub: 'Состояние всех компонентов', icon: 'dashboard', group: 'Обзор' },
|
||||||
{ id: 'redbtn', title: 'Автообход', sub: 'Роутер сам подберёт обход для каждого сервиса', icon: 'wand', group: 'Обход блокировок', dot: 'redbtn' },
|
{ id: 'redbtn', title: 'Автообход', sub: 'Роутер сам подберёт обход для каждого сервиса', icon: 'wand', group: 'Обход блокировок', dot: 'redbtn' },
|
||||||
{ id: 'steer', title: 'steer', sub: 'Выбранные сервисы через туннель WARP', icon: 'route', group: 'Обход блокировок', dot: 'steer' },
|
{ id: 'steer', title: 'Steer', sub: 'Выбранные сервисы через туннель WARP', icon: 'route', group: 'Обход блокировок', dot: 'steer' },
|
||||||
{ id: 'strategy', title: 'Zapret', sub: 'Стратегии, тесты, YouTube, игры, Discord и исключения', icon: 'shield', group: 'Обход блокировок', dot: 'zapret' },
|
{ id: 'strategy', title: 'Zapret', sub: 'Стратегии, тесты, YouTube, игры, Discord и исключения', icon: 'shield', group: 'Обход блокировок', dot: 'zapret' },
|
||||||
{ id: 'zapret2', title: 'Zapret2', sub: 'Установка и управление Zapret2', icon: 'bolt', group: 'Обход блокировок', dot: 'zapret2' },
|
{ id: 'zapret2', title: 'Zapret2', sub: 'Установка и управление Zapret2', icon: 'bolt', group: 'Обход блокировок', dot: 'zapret2' },
|
||||||
{ id: 'bytetube', title: 'ByeTube', sub: 'YouTube через ByeDPI', icon: 'play', group: 'Обход блокировок', dot: 'bytetube' },
|
{ id: 'bytetube', title: 'ByeTube', sub: 'YouTube через ByeDPI', icon: 'play', group: 'Обход блокировок', dot: 'bytetube' },
|
||||||
@@ -15611,10 +15611,11 @@ html[data-theme="light"] .zmw-top { background: linear-gradient(to bottom, rgba(
|
|||||||
.zmw-link-kvn {
|
.zmw-link-kvn {
|
||||||
color: #fff; border-color: transparent;
|
color: #fff; border-color: transparent;
|
||||||
background: var(--grad); background-size: 160% 100%;
|
background: var(--grad); background-size: 160% 100%;
|
||||||
|
background-origin: border-box; background-repeat: no-repeat;
|
||||||
box-shadow: 0 8px 20px -10px rgba(99,102,241,.9), inset 0 1px 0 rgba(255,255,255,.22);
|
box-shadow: 0 8px 20px -10px rgba(99,102,241,.9), inset 0 1px 0 rgba(255,255,255,.22);
|
||||||
transition: background-position .35s, transform .1s, box-shadow .2s;
|
transition: background-position .35s, transform .1s, box-shadow .2s;
|
||||||
}
|
}
|
||||||
.zmw-link-kvn:hover { color: #fff; border-color: transparent; background: var(--grad); background-size: 160% 100%; background-position: 100% 0; box-shadow: 0 10px 24px -10px rgba(99,102,241,1), inset 0 1px 0 rgba(255,255,255,.22); }
|
.zmw-link-kvn:hover { color: #fff; border-color: transparent; background: var(--grad); background-size: 160% 100%; background-origin: border-box; background-repeat: no-repeat; background-position: 100% 0; box-shadow: 0 10px 24px -10px rgba(99,102,241,1), inset 0 1px 0 rgba(255,255,255,.22); }
|
||||||
.zmw-link-tg { color: #229ed9; border-color: rgba(34,158,217,.35); background: rgba(34,158,217,.10); }
|
.zmw-link-tg { color: #229ed9; border-color: rgba(34,158,217,.35); background: rgba(34,158,217,.10); }
|
||||||
.zmw-link-tg:hover { color: #229ed9; border-color: rgba(34,158,217,.6); background: rgba(34,158,217,.16); }
|
.zmw-link-tg:hover { color: #229ed9; border-color: rgba(34,158,217,.6); background: rgba(34,158,217,.16); }
|
||||||
html[data-theme="dark"] .zmw-link-tg, html[data-theme="dark"] .zmw-link-tg:hover { color: #5cc1f0; }
|
html[data-theme="dark"] .zmw-link-tg, html[data-theme="dark"] .zmw-link-tg:hover { color: #5cc1f0; }
|
||||||
|
|||||||
Reference in new issue
Block a user