From afc2d5470ecba1efb9c66faf3288950de2f7b52e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=91=D0=BE=D0=B1?= =?UTF-8?q?=D1=8B=D1=80=D0=B5=D0=B2?= Date: Sun, 24 May 2026 21:54:23 +0300 Subject: [PATCH] fix(site): export preview/download and restore GeoExport deploy Restore site/ mirror, patch export fetch for empty API responses and Keenetic format, add Telegram bot card, and switch install.sh back to geoexport-site systemd + nginx on port 4173. Co-authored-by: Cursor --- install.sh | 131 ++++++++++++++--------------- site/.gitignore | 2 + site/README.md | 47 +++++++++++ site/assets/index-VQj200iM.js | 69 ++++++++++++++++ site/assets/index-uS40y9LP.css | 1 + site/assets/telegram-bot.js | 28 +++++++ site/data/last-update.json | 1 + site/data/presets.json | 1 + site/data/routing-presets.json | 1 + site/data/sources.json | 1 + site/index.html | 14 ++++ site/package.json | 15 ++++ site/patch-bundle.mjs | 53 ++++++++++++ site/server.mjs | 146 +++++++++++++++++++++++++++++++++ 14 files changed, 442 insertions(+), 68 deletions(-) create mode 100644 site/.gitignore create mode 100644 site/README.md create mode 100644 site/assets/index-VQj200iM.js create mode 100644 site/assets/index-uS40y9LP.css create mode 100644 site/assets/telegram-bot.js create mode 100644 site/data/last-update.json create mode 100644 site/data/presets.json create mode 100644 site/data/routing-presets.json create mode 100644 site/data/sources.json create mode 100644 site/index.html create mode 100644 site/package.json create mode 100644 site/patch-bundle.mjs create mode 100644 site/server.mjs diff --git a/install.sh b/install.sh index 81b9e86..8cde5a0 100755 --- a/install.sh +++ b/install.sh @@ -1,46 +1,33 @@ #!/usr/bin/env bash -# Domain Scanner — one-command install (Ubuntu/Debian VPS) +# GeoExport site — one-command install (Ubuntu/Debian VPS) set -euo pipefail -REPO_URL="${DOMAIN_SCANNER_REPO_URL:-https://github.com/andrey271192/Domain_web.git}" -BRANCH="${DOMAIN_SCANNER_BRANCH:-main}" -INSTALL_DIR="${DOMAIN_SCANNER_INSTALL_DIR:-/opt/domain_web}" -SERVICE_NAME="domain-scanner" -NGINX_SITE="domain-scanner" -APP_PORT="${DOMAIN_SCANNER_PORT:-3000}" +REPO_URL="${GEOEXPORT_REPO_URL:-https://github.com/andrey271192/Domain_web.git}" +BRANCH="${GEOEXPORT_BRANCH:-main}" +INSTALL_DIR="${GEOEXPORT_INSTALL_DIR:-/opt/domain_web}" +SITE_DIR="${INSTALL_DIR}/site" +SERVICE_NAME="geoexport-site" +NGINX_SITE="geoexport-site" +NODE_PORT="${GEOEXPORT_PORT:-4173}" export DEBIAN_FRONTEND=noninteractive -log() { echo "[domain-scanner-install] $*"; } +log() { echo "[geoexport-install] $*"; } need_root() { if [[ "${EUID:-$(id -u)}" -ne 0 ]]; then - echo "Run as root: sudo bash install.sh" >&2 + echo "Запустите от root: sudo bash install.sh" >&2 exit 1 fi } install_packages() { - log "Installing nginx, git, curl, docker..." + log "Установка пакетов (nginx, git, curl, nodejs)..." apt-get update -qq - apt-get install -y -qq nginx git curl ca-certificates docker.io docker-compose >/dev/null 2>&1 || \ - apt-get install -y -qq nginx git curl ca-certificates docker.io >/dev/null - if ! command -v docker-compose >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then - ln -sf "$(command -v docker)" /usr/local/bin/docker-compose 2>/dev/null || true - fi - systemctl enable docker - systemctl start docker -} - -compose_cmd() { - if docker compose version >/dev/null 2>&1; then - echo "docker compose" - else - echo "docker-compose" - fi + apt-get install -y -qq nginx git curl ca-certificates nodejs npm >/dev/null } clone_or_update() { - log "Cloning/updating ${INSTALL_DIR}..." + log "Клонирование/обновление репозитория в ${INSTALL_DIR}..." if [[ -d "${INSTALL_DIR}/.git" ]]; then git -C "${INSTALL_DIR}" fetch origin "${BRANCH}" git -C "${INSTALL_DIR}" checkout "${BRANCH}" @@ -49,59 +36,64 @@ clone_or_update() { rm -rf "${INSTALL_DIR}" git clone --depth 1 --branch "${BRANCH}" "${REPO_URL}" "${INSTALL_DIR}" fi -} - -setup_env() { - log "Writing .env..." - if [[ ! -f "${INSTALL_DIR}/.env" ]]; then - SECRET=$(openssl rand -base64 32 2>/dev/null || head -c 32 /dev/urandom | base64) - cat >"${INSTALL_DIR}/.env" <&2 + exit 1 fi } -deploy_docker() { - log "Building and starting Docker stack..." - cd "${INSTALL_DIR}" - COMPOSE=$(compose_cmd) - $COMPOSE build --quiet - $COMPOSE up -d - sleep 12 - $COMPOSE exec -T web npx prisma db push 2>/dev/null || true +apply_patches() { + log "Применение патчей фронтенда (export preview/download)..." + node "${SITE_DIR}/patch-bundle.mjs" +} + +install_systemd() { + log "Настройка systemd (${SERVICE_NAME})..." + cat >"/etc/systemd/system/${SERVICE_NAME}.service" <"/etc/nginx/sites-available/${NGINX_SITE}" </dev/null || true - rm -f /etc/nginx/sites-available/geoexport-site 2>/dev/null || true - systemctl stop geoexport-site.service 2>/dev/null || true - systemctl disable geoexport-site.service 2>/dev/null || true - rm -f /etc/systemd/system/geoexport-site.service 2>/dev/null || true - systemctl daemon-reload + rm -f /etc/nginx/sites-enabled/domain-scanner 2>/dev/null || true + rm -f /etc/nginx/sites-available/domain-scanner 2>/dev/null || true rm -f /etc/nginx/sites-enabled/default 2>/dev/null || true ln -sf "/etc/nginx/sites-available/${NGINX_SITE}" "/etc/nginx/sites-enabled/${NGINX_SITE}" nginx -t @@ -110,24 +102,27 @@ EOF } verify() { - log "Verifying..." - sleep 3 - if ! curl -fsS -o /dev/null "http://127.0.0.1:${APP_PORT}/"; then - echo "App not responding on port ${APP_PORT}" >&2 - $(compose_cmd) -f "${INSTALL_DIR}/docker-compose.yml" logs --tail=50 web || true + log "Проверка..." + sleep 2 + if ! systemctl is-active --quiet "${SERVICE_NAME}.service"; then + systemctl status "${SERVICE_NAME}.service" --no-pager || true exit 1 fi - TITLE=$(curl -fsS "http://127.0.0.1/" | head -c 2000 | grep -oi 'Domain Scanner' | head -1 || true) - if [[ -z "${TITLE}" ]]; then - log "Warning: page title may not include Domain Scanner yet" + if ! curl -fsS -o /dev/null "http://127.0.0.1:${NODE_PORT}/"; then + echo "Node-сервер не отвечает на порту ${NODE_PORT}" >&2 + exit 1 fi - log "Done. Open http://$(hostname -I | awk '{print $1}')/" + if ! curl -fsS -o /dev/null "http://127.0.0.1/"; then + echo "nginx не отдаёт сайт на :80" >&2 + exit 1 + fi + log "Готово. Сайт доступен по http://$(hostname -I | awk '{print $1}')/" } need_root install_packages clone_or_update -setup_env -deploy_docker +apply_patches +install_systemd install_nginx verify diff --git a/site/.gitignore b/site/.gitignore new file mode 100644 index 0000000..2752eb9 --- /dev/null +++ b/site/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +.DS_Store diff --git a/site/README.md b/site/README.md new file mode 100644 index 0000000..a59d1ea --- /dev/null +++ b/site/README.md @@ -0,0 +1,47 @@ +# GeoExport — локальная копия + +Зеркало интерфейса [geoexport.org](https://geoexport.org/): экспорт списков geoip/geosite для VPN-клиентов (Xray, 3x-ui, V2Ray). + +## Запуск + +```bash +cd ~/geoexport-clone +npm start +``` + +Откройте в браузере: **http://127.0.0.1:4173** + +Другой порт: `PORT=8080 npm start` + +## Состав + +| Путь | Назначение | +|------|------------| +| `index.html` | Точка входа SPA | +| `assets/` | Собранный фронтенд (React + Tailwind) с оригинала | +| `data/*.json` | Кэш API: пресеты, источники, дата обновления | +| `server.mjs` | Локальный сервер: статика + JSON + прокси динамических API | + +## Обновление данных + +```bash +curl -sL https://geoexport.org/api/presets -o data/presets.json +curl -sL https://geoexport.org/api/sources -o data/sources.json +curl -sL https://geoexport.org/api/last-update -o data/last-update.json +curl -sL https://geoexport.org/api/routing/presets -o data/routing-presets.json +``` + +## Патч экспорта + +Перед запуском применяется `patch-bundle.mjs` (превью/скачивание, Keenetic, пустые ответы API). + +```bash +npm start # prestart → patch автоматически +``` + +Карточка про Telegram-бота `@domain_searchPro_bot` — `assets/telegram-bot.js`. + +## Ограничения + +- Экспорт, поиск, DNS lookup и генерация routing проксируются на живой `geoexport.org` (нужен интернет). +- Это копия UI и кэшированных справочников, не полный автономный бэкенд с базами `.dat`. diff --git a/site/assets/index-VQj200iM.js b/site/assets/index-VQj200iM.js new file mode 100644 index 0000000..ee8d28d --- /dev/null +++ b/site/assets/index-VQj200iM.js @@ -0,0 +1,69 @@ +function od(e,t){for(var n=0;nr[l]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const l of document.querySelectorAll('link[rel="modulepreload"]'))r(l);new MutationObserver(l=>{for(const s of l)if(s.type==="childList")for(const o of s.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&r(o)}).observe(document,{childList:!0,subtree:!0});function n(l){const s={};return l.integrity&&(s.integrity=l.integrity),l.referrerPolicy&&(s.referrerPolicy=l.referrerPolicy),l.crossOrigin==="use-credentials"?s.credentials="include":l.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function r(l){if(l.ep)return;l.ep=!0;const s=n(l);fetch(l.href,s)}})();function id(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var Ma={exports:{}},El={},$a={exports:{}},M={};/** + * @license React + * react.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var pr=Symbol.for("react.element"),ad=Symbol.for("react.portal"),ud=Symbol.for("react.fragment"),cd=Symbol.for("react.strict_mode"),dd=Symbol.for("react.profiler"),fd=Symbol.for("react.provider"),pd=Symbol.for("react.context"),md=Symbol.for("react.forward_ref"),hd=Symbol.for("react.suspense"),vd=Symbol.for("react.memo"),gd=Symbol.for("react.lazy"),gi=Symbol.iterator;function xd(e){return e===null||typeof e!="object"?null:(e=gi&&e[gi]||e["@@iterator"],typeof e=="function"?e:null)}var Da={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},Fa=Object.assign,Ba={};function wn(e,t,n){this.props=e,this.context=t,this.refs=Ba,this.updater=n||Da}wn.prototype.isReactComponent={};wn.prototype.setState=function(e,t){if(typeof e!="object"&&typeof e!="function"&&e!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")};wn.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function Aa(){}Aa.prototype=wn.prototype;function xo(e,t,n){this.props=e,this.context=t,this.refs=Ba,this.updater=n||Da}var yo=xo.prototype=new Aa;yo.constructor=xo;Fa(yo,wn.prototype);yo.isPureReactComponent=!0;var xi=Array.isArray,Va=Object.prototype.hasOwnProperty,ko={current:null},Wa={key:!0,ref:!0,__self:!0,__source:!0};function Ha(e,t,n){var r,l={},s=null,o=null;if(t!=null)for(r in t.ref!==void 0&&(o=t.ref),t.key!==void 0&&(s=""+t.key),t)Va.call(t,r)&&!Wa.hasOwnProperty(r)&&(l[r]=t[r]);var i=arguments.length-2;if(i===1)l.children=n;else if(1>>1,ne=L[Y];if(0>>1;Yl(Hl,I))Ltl(jr,Hl)?(L[Y]=jr,L[Lt]=I,Y=Lt):(L[Y]=Hl,L[Pt]=I,Y=Pt);else if(Ltl(jr,I))L[Y]=jr,L[Lt]=I,Y=Lt;else break e}}return O}function l(L,O){var I=L.sortIndex-O.sortIndex;return I!==0?I:L.id-O.id}if(typeof performance=="object"&&typeof performance.now=="function"){var s=performance;e.unstable_now=function(){return s.now()}}else{var o=Date,i=o.now();e.unstable_now=function(){return o.now()-i}}var a=[],c=[],h=1,m=null,v=3,g=!1,y=!1,j=!1,C=typeof setTimeout=="function"?setTimeout:null,f=typeof clearTimeout=="function"?clearTimeout:null,d=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function p(L){for(var O=n(c);O!==null;){if(O.callback===null)r(c);else if(O.startTime<=L)r(c),O.sortIndex=O.expirationTime,t(a,O);else break;O=n(c)}}function x(L){if(j=!1,p(L),!y)if(n(a)!==null)y=!0,F(E);else{var O=n(c);O!==null&&V(x,O.startTime-L)}}function E(L,O){y=!1,j&&(j=!1,f(P),P=-1),g=!0;var I=v;try{for(p(O),m=n(a);m!==null&&(!(m.expirationTime>O)||L&&!$());){var Y=m.callback;if(typeof Y=="function"){m.callback=null,v=m.priorityLevel;var ne=Y(m.expirationTime<=O);O=e.unstable_now(),typeof ne=="function"?m.callback=ne:m===n(a)&&r(a),p(O)}else r(a);m=n(a)}if(m!==null)var Sr=!0;else{var Pt=n(c);Pt!==null&&V(x,Pt.startTime-O),Sr=!1}return Sr}finally{m=null,v=I,g=!1}}var w=!1,S=null,P=-1,z=5,T=-1;function $(){return!(e.unstable_now()-TL||125Y?(L.sortIndex=I,t(c,L),n(a)===null&&L===n(c)&&(j?(f(P),P=-1):j=!0,V(x,I-Y))):(L.sortIndex=ne,t(a,L),y||g||(y=!0,F(E))),L},e.unstable_shouldYield=$,e.unstable_wrapCallback=function(L){var O=v;return function(){var I=v;v=O;try{return L.apply(this,arguments)}finally{v=I}}}})(Xa);Ga.exports=Xa;var Rd=Ga.exports;/** + * @license React + * react-dom.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var Td=k,Ce=Rd;function _(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),ws=Object.prototype.hasOwnProperty,zd=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,ki={},wi={};function Od(e){return ws.call(wi,e)?!0:ws.call(ki,e)?!1:zd.test(e)?wi[e]=!0:(ki[e]=!0,!1)}function Id(e,t,n,r){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return r?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function Ud(e,t,n,r){if(t===null||typeof t>"u"||Id(e,t,n,r))return!0;if(r)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function ge(e,t,n,r,l,s,o){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=l,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=s,this.removeEmptyString=o}var ie={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){ie[e]=new ge(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];ie[t]=new ge(t,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){ie[e]=new ge(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){ie[e]=new ge(e,2,!1,e,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){ie[e]=new ge(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){ie[e]=new ge(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){ie[e]=new ge(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){ie[e]=new ge(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){ie[e]=new ge(e,5,!1,e.toLowerCase(),null,!1,!1)});var So=/[\-:]([a-z])/g;function jo(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(So,jo);ie[t]=new ge(t,1,!1,e,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(So,jo);ie[t]=new ge(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(So,jo);ie[t]=new ge(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){ie[e]=new ge(e,1,!1,e.toLowerCase(),null,!1,!1)});ie.xlinkHref=new ge("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){ie[e]=new ge(e,1,!1,e.toLowerCase(),null,!0,!0)});function No(e,t,n,r){var l=ie.hasOwnProperty(t)?ie[t]:null;(l!==null?l.type!==0:r||!(2i||l[o]!==s[i]){var a=` +`+l[o].replace(" at new "," at ");return e.displayName&&a.includes("")&&(a=a.replace("",e.displayName)),a}while(1<=o&&0<=i);break}}}finally{Kl=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?Un(e):""}function Md(e){switch(e.tag){case 5:return Un(e.type);case 16:return Un("Lazy");case 13:return Un("Suspense");case 19:return Un("SuspenseList");case 0:case 2:case 15:return e=Gl(e.type,!1),e;case 11:return e=Gl(e.type.render,!1),e;case 1:return e=Gl(e.type,!0),e;default:return""}}function _s(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case Xt:return"Fragment";case Gt:return"Portal";case Ss:return"Profiler";case _o:return"StrictMode";case js:return"Suspense";case Ns:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case Za:return(e.displayName||"Context")+".Consumer";case Ja:return(e._context.displayName||"Context")+".Provider";case Eo:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case Co:return t=e.displayName||null,t!==null?t:_s(e.type)||"Memo";case st:t=e._payload,e=e._init;try{return _s(e(t))}catch{}}return null}function $d(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return _s(t);case 8:return t===_o?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function wt(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function eu(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function Dd(e){var t=eu(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var l=n.get,s=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return l.call(this)},set:function(o){r=""+o,s.call(this,o)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(o){r=""+o},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function Er(e){e._valueTracker||(e._valueTracker=Dd(e))}function tu(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=eu(e)?e.checked?"true":"false":e.value),e=r,e!==n?(t.setValue(e),!0):!1}function el(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function Es(e,t){var n=t.checked;return G({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function ji(e,t){var n=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;n=wt(t.value!=null?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function nu(e,t){t=t.checked,t!=null&&No(e,"checked",t,!1)}function Cs(e,t){nu(e,t);var n=wt(t.value),r=t.type;if(n!=null)r==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?Ps(e,t.type,n):t.hasOwnProperty("defaultValue")&&Ps(e,t.type,wt(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function Ni(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function Ps(e,t,n){(t!=="number"||el(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var Mn=Array.isArray;function on(e,t,n,r){if(e=e.options,t){t={};for(var l=0;l"+t.valueOf().toString()+"",t=Cr.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function Xn(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var Fn={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},Fd=["Webkit","ms","Moz","O"];Object.keys(Fn).forEach(function(e){Fd.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),Fn[t]=Fn[e]})});function ou(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||Fn.hasOwnProperty(e)&&Fn[e]?(""+t).trim():t+"px"}function iu(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=n.indexOf("--")===0,l=ou(n,t[n],r);n==="float"&&(n="cssFloat"),r?e.setProperty(n,l):e[n]=l}}var Bd=G({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function Ts(e,t){if(t){if(Bd[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(_(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(_(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(_(61))}if(t.style!=null&&typeof t.style!="object")throw Error(_(62))}}function zs(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var Os=null;function Po(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var Is=null,an=null,un=null;function Ci(e){if(e=vr(e)){if(typeof Is!="function")throw Error(_(280));var t=e.stateNode;t&&(t=Tl(t),Is(e.stateNode,e.type,t))}}function au(e){an?un?un.push(e):un=[e]:an=e}function uu(){if(an){var e=an,t=un;if(un=an=null,Ci(e),t)for(e=0;e>>=0,e===0?32:31-(Jd(e)/Zd|0)|0}var Pr=64,Lr=4194304;function $n(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function ll(e,t){var n=e.pendingLanes;if(n===0)return 0;var r=0,l=e.suspendedLanes,s=e.pingedLanes,o=n&268435455;if(o!==0){var i=o&~l;i!==0?r=$n(i):(s&=o,s!==0&&(r=$n(s)))}else o=n&~l,o!==0?r=$n(o):s!==0&&(r=$n(s));if(r===0)return 0;if(t!==0&&t!==r&&!(t&l)&&(l=r&-r,s=t&-t,l>=s||l===16&&(s&4194240)!==0))return t;if(r&4&&(r|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0n;n++)t.push(e);return t}function mr(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-Be(t),e[t]=n}function nf(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=An),Mi=" ",$i=!1;function Lu(e,t){switch(e){case"keyup":return Tf.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Ru(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var Yt=!1;function Of(e,t){switch(e){case"compositionend":return Ru(t);case"keypress":return t.which!==32?null:($i=!0,Mi);case"textInput":return e=t.data,e===Mi&&$i?null:e;default:return null}}function If(e,t){if(Yt)return e==="compositionend"||!Mo&&Lu(e,t)?(e=Cu(),Hr=Oo=ut=null,Yt=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=Ai(n)}}function Iu(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Iu(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Uu(){for(var e=window,t=el();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=el(e.document)}return t}function $o(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function Wf(e){var t=Uu(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&Iu(n.ownerDocument.documentElement,n)){if(r!==null&&$o(n)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var l=n.textContent.length,s=Math.min(r.start,l);r=r.end===void 0?s:Math.min(r.end,l),!e.extend&&s>r&&(l=r,r=s,s=l),l=Vi(n,s);var o=Vi(n,r);l&&o&&(e.rangeCount!==1||e.anchorNode!==l.node||e.anchorOffset!==l.offset||e.focusNode!==o.node||e.focusOffset!==o.offset)&&(t=t.createRange(),t.setStart(l.node,l.offset),e.removeAllRanges(),s>r?(e.addRange(t),e.extend(o.node,o.offset)):(t.setEnd(o.node,o.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,Jt=null,Bs=null,Wn=null,As=!1;function Wi(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;As||Jt==null||Jt!==el(r)||(r=Jt,"selectionStart"in r&&$o(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),Wn&&tr(Wn,r)||(Wn=r,r=il(Bs,"onSelect"),0en||(e.current=Ks[en],Ks[en]=null,en--)}function A(e,t){en++,Ks[en]=e.current,e.current=t}var St={},fe=Nt(St),ke=Nt(!1),$t=St;function mn(e,t){var n=e.type.contextTypes;if(!n)return St;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var l={},s;for(s in n)l[s]=t[s];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=l),l}function we(e){return e=e.childContextTypes,e!=null}function ul(){H(ke),H(fe)}function Yi(e,t,n){if(fe.current!==St)throw Error(_(168));A(fe,t),A(ke,n)}function Hu(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return n;r=r.getChildContext();for(var l in r)if(!(l in t))throw Error(_(108,$d(e)||"Unknown",l));return G({},n,r)}function cl(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||St,$t=fe.current,A(fe,e),A(ke,ke.current),!0}function Ji(e,t,n){var r=e.stateNode;if(!r)throw Error(_(169));n?(e=Hu(e,t,$t),r.__reactInternalMemoizedMergedChildContext=e,H(ke),H(fe),A(fe,e)):H(ke),A(ke,n)}var Xe=null,zl=!1,as=!1;function bu(e){Xe===null?Xe=[e]:Xe.push(e)}function tp(e){zl=!0,bu(e)}function _t(){if(!as&&Xe!==null){as=!0;var e=0,t=B;try{var n=Xe;for(B=1;e>=o,l-=o,Ye=1<<32-Be(t)+l|n<P?(z=S,S=null):z=S.sibling;var T=v(f,S,p[P],x);if(T===null){S===null&&(S=z);break}e&&S&&T.alternate===null&&t(f,S),d=s(T,d,P),w===null?E=T:w.sibling=T,w=T,S=z}if(P===p.length)return n(f,S),b&&Rt(f,P),E;if(S===null){for(;PP?(z=S,S=null):z=S.sibling;var $=v(f,S,T.value,x);if($===null){S===null&&(S=z);break}e&&S&&$.alternate===null&&t(f,S),d=s($,d,P),w===null?E=$:w.sibling=$,w=$,S=z}if(T.done)return n(f,S),b&&Rt(f,P),E;if(S===null){for(;!T.done;P++,T=p.next())T=m(f,T.value,x),T!==null&&(d=s(T,d,P),w===null?E=T:w.sibling=T,w=T);return b&&Rt(f,P),E}for(S=r(f,S);!T.done;P++,T=p.next())T=g(S,f,P,T.value,x),T!==null&&(e&&T.alternate!==null&&S.delete(T.key===null?P:T.key),d=s(T,d,P),w===null?E=T:w.sibling=T,w=T);return e&&S.forEach(function(Et){return t(f,Et)}),b&&Rt(f,P),E}function C(f,d,p,x){if(typeof p=="object"&&p!==null&&p.type===Xt&&p.key===null&&(p=p.props.children),typeof p=="object"&&p!==null){switch(p.$$typeof){case _r:e:{for(var E=p.key,w=d;w!==null;){if(w.key===E){if(E=p.type,E===Xt){if(w.tag===7){n(f,w.sibling),d=l(w,p.props.children),d.return=f,f=d;break e}}else if(w.elementType===E||typeof E=="object"&&E!==null&&E.$$typeof===st&&ea(E)===w.type){n(f,w.sibling),d=l(w,p.props),d.ref=Rn(f,w,p),d.return=f,f=d;break e}n(f,w);break}else t(f,w);w=w.sibling}p.type===Xt?(d=Mt(p.props.children,f.mode,x,p.key),d.return=f,f=d):(x=Zr(p.type,p.key,p.props,null,f.mode,x),x.ref=Rn(f,d,p),x.return=f,f=x)}return o(f);case Gt:e:{for(w=p.key;d!==null;){if(d.key===w)if(d.tag===4&&d.stateNode.containerInfo===p.containerInfo&&d.stateNode.implementation===p.implementation){n(f,d.sibling),d=l(d,p.children||[]),d.return=f,f=d;break e}else{n(f,d);break}else t(f,d);d=d.sibling}d=vs(p,f.mode,x),d.return=f,f=d}return o(f);case st:return w=p._init,C(f,d,w(p._payload),x)}if(Mn(p))return y(f,d,p,x);if(_n(p))return j(f,d,p,x);Mr(f,p)}return typeof p=="string"&&p!==""||typeof p=="number"?(p=""+p,d!==null&&d.tag===6?(n(f,d.sibling),d=l(d,p),d.return=f,f=d):(n(f,d),d=hs(p,f.mode,x),d.return=f,f=d),o(f)):n(f,d)}return C}var vn=Xu(!0),Yu=Xu(!1),pl=Nt(null),ml=null,rn=null,Ao=null;function Vo(){Ao=rn=ml=null}function Wo(e){var t=pl.current;H(pl),e._currentValue=t}function Ys(e,t,n){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,r!==null&&(r.childLanes|=t)):r!==null&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===n)break;e=e.return}}function dn(e,t){ml=e,Ao=rn=null,e=e.dependencies,e!==null&&e.firstContext!==null&&(e.lanes&t&&(ye=!0),e.firstContext=null)}function Ie(e){var t=e._currentValue;if(Ao!==e)if(e={context:e,memoizedValue:t,next:null},rn===null){if(ml===null)throw Error(_(308));rn=e,ml.dependencies={lanes:0,firstContext:e}}else rn=rn.next=e;return t}var Ot=null;function Ho(e){Ot===null?Ot=[e]:Ot.push(e)}function Ju(e,t,n,r){var l=t.interleaved;return l===null?(n.next=n,Ho(t)):(n.next=l.next,l.next=n),t.interleaved=n,tt(e,r)}function tt(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var ot=!1;function bo(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Zu(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Ze(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function vt(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,D&2){var l=r.pending;return l===null?t.next=t:(t.next=l.next,l.next=t),r.pending=t,tt(e,n)}return l=r.interleaved,l===null?(t.next=t,Ho(r)):(t.next=l.next,l.next=t),r.interleaved=t,tt(e,n)}function Qr(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,Ro(e,n)}}function ta(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var l=null,s=null;if(n=n.firstBaseUpdate,n!==null){do{var o={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};s===null?l=s=o:s=s.next=o,n=n.next}while(n!==null);s===null?l=s=t:s=s.next=t}else l=s=t;n={baseState:r.baseState,firstBaseUpdate:l,lastBaseUpdate:s,shared:r.shared,effects:r.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function hl(e,t,n,r){var l=e.updateQueue;ot=!1;var s=l.firstBaseUpdate,o=l.lastBaseUpdate,i=l.shared.pending;if(i!==null){l.shared.pending=null;var a=i,c=a.next;a.next=null,o===null?s=c:o.next=c,o=a;var h=e.alternate;h!==null&&(h=h.updateQueue,i=h.lastBaseUpdate,i!==o&&(i===null?h.firstBaseUpdate=c:i.next=c,h.lastBaseUpdate=a))}if(s!==null){var m=l.baseState;o=0,h=c=a=null,i=s;do{var v=i.lane,g=i.eventTime;if((r&v)===v){h!==null&&(h=h.next={eventTime:g,lane:0,tag:i.tag,payload:i.payload,callback:i.callback,next:null});e:{var y=e,j=i;switch(v=t,g=n,j.tag){case 1:if(y=j.payload,typeof y=="function"){m=y.call(g,m,v);break e}m=y;break e;case 3:y.flags=y.flags&-65537|128;case 0:if(y=j.payload,v=typeof y=="function"?y.call(g,m,v):y,v==null)break e;m=G({},m,v);break e;case 2:ot=!0}}i.callback!==null&&i.lane!==0&&(e.flags|=64,v=l.effects,v===null?l.effects=[i]:v.push(i))}else g={eventTime:g,lane:v,tag:i.tag,payload:i.payload,callback:i.callback,next:null},h===null?(c=h=g,a=m):h=h.next=g,o|=v;if(i=i.next,i===null){if(i=l.shared.pending,i===null)break;v=i,i=v.next,v.next=null,l.lastBaseUpdate=v,l.shared.pending=null}}while(!0);if(h===null&&(a=m),l.baseState=a,l.firstBaseUpdate=c,l.lastBaseUpdate=h,t=l.shared.interleaved,t!==null){l=t;do o|=l.lane,l=l.next;while(l!==t)}else s===null&&(l.shared.lanes=0);Bt|=o,e.lanes=o,e.memoizedState=m}}function na(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var r=cs.transition;cs.transition={};try{e(!1),t()}finally{B=n,cs.transition=r}}function hc(){return Ue().memoizedState}function sp(e,t,n){var r=xt(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},vc(e))gc(t,n);else if(n=Ju(e,t,n,r),n!==null){var l=he();Ae(n,e,r,l),xc(n,t,r)}}function op(e,t,n){var r=xt(e),l={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if(vc(e))gc(t,l);else{var s=e.alternate;if(e.lanes===0&&(s===null||s.lanes===0)&&(s=t.lastRenderedReducer,s!==null))try{var o=t.lastRenderedState,i=s(o,n);if(l.hasEagerState=!0,l.eagerState=i,Ve(i,o)){var a=t.interleaved;a===null?(l.next=l,Ho(t)):(l.next=a.next,a.next=l),t.interleaved=l;return}}catch{}finally{}n=Ju(e,t,l,r),n!==null&&(l=he(),Ae(n,e,r,l),xc(n,t,r))}}function vc(e){var t=e.alternate;return e===K||t!==null&&t===K}function gc(e,t){Hn=gl=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function xc(e,t,n){if(n&4194240){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,Ro(e,n)}}var xl={readContext:Ie,useCallback:ae,useContext:ae,useEffect:ae,useImperativeHandle:ae,useInsertionEffect:ae,useLayoutEffect:ae,useMemo:ae,useReducer:ae,useRef:ae,useState:ae,useDebugValue:ae,useDeferredValue:ae,useTransition:ae,useMutableSource:ae,useSyncExternalStore:ae,useId:ae,unstable_isNewReconciler:!1},ip={readContext:Ie,useCallback:function(e,t){return He().memoizedState=[e,t===void 0?null:t],e},useContext:Ie,useEffect:la,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,Gr(4194308,4,cc.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Gr(4194308,4,e,t)},useInsertionEffect:function(e,t){return Gr(4,2,e,t)},useMemo:function(e,t){var n=He();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=He();return t=n!==void 0?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=sp.bind(null,K,e),[r.memoizedState,e]},useRef:function(e){var t=He();return e={current:e},t.memoizedState=e},useState:ra,useDebugValue:qo,useDeferredValue:function(e){return He().memoizedState=e},useTransition:function(){var e=ra(!1),t=e[0];return e=lp.bind(null,e[1]),He().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=K,l=He();if(b){if(n===void 0)throw Error(_(407));n=n()}else{if(n=t(),le===null)throw Error(_(349));Ft&30||nc(r,t,n)}l.memoizedState=n;var s={value:n,getSnapshot:t};return l.queue=s,la(lc.bind(null,r,s,e),[e]),r.flags|=2048,ur(9,rc.bind(null,r,s,n,t),void 0,null),n},useId:function(){var e=He(),t=le.identifierPrefix;if(b){var n=Je,r=Ye;n=(r&~(1<<32-Be(r)-1)).toString(32)+n,t=":"+t+"R"+n,n=ir++,0<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=o.createElement(n,{is:r.is}):(e=o.createElement(n),n==="select"&&(o=e,r.multiple?o.multiple=!0:r.size&&(o.size=r.size))):e=o.createElementNS(e,n),e[be]=t,e[lr]=r,Pc(e,t,!1,!1),t.stateNode=e;e:{switch(o=zs(n,r),n){case"dialog":W("cancel",e),W("close",e),l=r;break;case"iframe":case"object":case"embed":W("load",e),l=r;break;case"video":case"audio":for(l=0;lyn&&(t.flags|=128,r=!0,Tn(s,!1),t.lanes=4194304)}else{if(!r)if(e=vl(o),e!==null){if(t.flags|=128,r=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),Tn(s,!0),s.tail===null&&s.tailMode==="hidden"&&!o.alternate&&!b)return ue(t),null}else 2*J()-s.renderingStartTime>yn&&n!==1073741824&&(t.flags|=128,r=!0,Tn(s,!1),t.lanes=4194304);s.isBackwards?(o.sibling=t.child,t.child=o):(n=s.last,n!==null?n.sibling=o:t.child=o,s.last=o)}return s.tail!==null?(t=s.tail,s.rendering=t,s.tail=t.sibling,s.renderingStartTime=J(),t.sibling=null,n=Q.current,A(Q,r?n&1|2:n&1),t):(ue(t),null);case 22:case 23:return si(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&t.mode&1?je&1073741824&&(ue(t),t.subtreeFlags&6&&(t.flags|=8192)):ue(t),null;case 24:return null;case 25:return null}throw Error(_(156,t.tag))}function hp(e,t){switch(Fo(t),t.tag){case 1:return we(t.type)&&ul(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return gn(),H(ke),H(fe),Go(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 5:return Ko(t),null;case 13:if(H(Q),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(_(340));hn()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return H(Q),null;case 4:return gn(),null;case 10:return Wo(t.type._context),null;case 22:case 23:return si(),null;case 24:return null;default:return null}}var Dr=!1,ce=!1,vp=typeof WeakSet=="function"?WeakSet:Set,R=null;function ln(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(r){X(e,t,r)}else n.current=null}function so(e,t,n){try{n()}catch(r){X(e,t,r)}}var ha=!1;function gp(e,t){if(Vs=sl,e=Uu(),$o(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var l=r.anchorOffset,s=r.focusNode;r=r.focusOffset;try{n.nodeType,s.nodeType}catch{n=null;break e}var o=0,i=-1,a=-1,c=0,h=0,m=e,v=null;t:for(;;){for(var g;m!==n||l!==0&&m.nodeType!==3||(i=o+l),m!==s||r!==0&&m.nodeType!==3||(a=o+r),m.nodeType===3&&(o+=m.nodeValue.length),(g=m.firstChild)!==null;)v=m,m=g;for(;;){if(m===e)break t;if(v===n&&++c===l&&(i=o),v===s&&++h===r&&(a=o),(g=m.nextSibling)!==null)break;m=v,v=m.parentNode}m=g}n=i===-1||a===-1?null:{start:i,end:a}}else n=null}n=n||{start:0,end:0}}else n=null;for(Ws={focusedElem:e,selectionRange:n},sl=!1,R=t;R!==null;)if(t=R,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,R=e;else for(;R!==null;){t=R;try{var y=t.alternate;if(t.flags&1024)switch(t.tag){case 0:case 11:case 15:break;case 1:if(y!==null){var j=y.memoizedProps,C=y.memoizedState,f=t.stateNode,d=f.getSnapshotBeforeUpdate(t.elementType===t.type?j:$e(t.type,j),C);f.__reactInternalSnapshotBeforeUpdate=d}break;case 3:var p=t.stateNode.containerInfo;p.nodeType===1?p.textContent="":p.nodeType===9&&p.documentElement&&p.removeChild(p.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(_(163))}}catch(x){X(t,t.return,x)}if(e=t.sibling,e!==null){e.return=t.return,R=e;break}R=t.return}return y=ha,ha=!1,y}function bn(e,t,n){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var l=r=r.next;do{if((l.tag&e)===e){var s=l.destroy;l.destroy=void 0,s!==void 0&&so(t,n,s)}l=l.next}while(l!==r)}}function Ul(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function oo(e){var t=e.ref;if(t!==null){var n=e.stateNode;switch(e.tag){case 5:e=n;break;default:e=n}typeof t=="function"?t(e):t.current=e}}function Tc(e){var t=e.alternate;t!==null&&(e.alternate=null,Tc(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[be],delete t[lr],delete t[Qs],delete t[qf],delete t[ep])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function zc(e){return e.tag===5||e.tag===3||e.tag===4}function va(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||zc(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function io(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=al));else if(r!==4&&(e=e.child,e!==null))for(io(e,t,n),e=e.sibling;e!==null;)io(e,t,n),e=e.sibling}function ao(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for(ao(e,t,n),e=e.sibling;e!==null;)ao(e,t,n),e=e.sibling}var se=null,De=!1;function lt(e,t,n){for(n=n.child;n!==null;)Oc(e,t,n),n=n.sibling}function Oc(e,t,n){if(Qe&&typeof Qe.onCommitFiberUnmount=="function")try{Qe.onCommitFiberUnmount(Cl,n)}catch{}switch(n.tag){case 5:ce||ln(n,t);case 6:var r=se,l=De;se=null,lt(e,t,n),se=r,De=l,se!==null&&(De?(e=se,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):se.removeChild(n.stateNode));break;case 18:se!==null&&(De?(e=se,n=n.stateNode,e.nodeType===8?is(e.parentNode,n):e.nodeType===1&&is(e,n),qn(e)):is(se,n.stateNode));break;case 4:r=se,l=De,se=n.stateNode.containerInfo,De=!0,lt(e,t,n),se=r,De=l;break;case 0:case 11:case 14:case 15:if(!ce&&(r=n.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){l=r=r.next;do{var s=l,o=s.destroy;s=s.tag,o!==void 0&&(s&2||s&4)&&so(n,t,o),l=l.next}while(l!==r)}lt(e,t,n);break;case 1:if(!ce&&(ln(n,t),r=n.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(i){X(n,t,i)}lt(e,t,n);break;case 21:lt(e,t,n);break;case 22:n.mode&1?(ce=(r=ce)||n.memoizedState!==null,lt(e,t,n),ce=r):lt(e,t,n);break;default:lt(e,t,n)}}function ga(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new vp),t.forEach(function(r){var l=Ep.bind(null,e,r);n.has(r)||(n.add(r),r.then(l,l))})}}function Me(e,t){var n=t.deletions;if(n!==null)for(var r=0;rl&&(l=o),r&=~s}if(r=l,r=J()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*yp(r/1960))-r,10e?16:e,ct===null)var r=!1;else{if(e=ct,ct=null,wl=0,D&6)throw Error(_(331));var l=D;for(D|=4,R=e.current;R!==null;){var s=R,o=s.child;if(R.flags&16){var i=s.deletions;if(i!==null){for(var a=0;aJ()-ri?Ut(e,0):ni|=n),Se(e,t)}function Ac(e,t){t===0&&(e.mode&1?(t=Lr,Lr<<=1,!(Lr&130023424)&&(Lr=4194304)):t=1);var n=he();e=tt(e,t),e!==null&&(mr(e,t,n),Se(e,n))}function _p(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),Ac(e,n)}function Ep(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,l=e.memoizedState;l!==null&&(n=l.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(_(314))}r!==null&&r.delete(t),Ac(e,n)}var Vc;Vc=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||ke.current)ye=!0;else{if(!(e.lanes&n)&&!(t.flags&128))return ye=!1,pp(e,t,n);ye=!!(e.flags&131072)}else ye=!1,b&&t.flags&1048576&&Qu(t,fl,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;Xr(e,t),e=t.pendingProps;var l=mn(t,fe.current);dn(t,n),l=Yo(null,t,r,e,l,n);var s=Jo();return t.flags|=1,typeof l=="object"&&l!==null&&typeof l.render=="function"&&l.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,we(r)?(s=!0,cl(t)):s=!1,t.memoizedState=l.state!==null&&l.state!==void 0?l.state:null,bo(t),l.updater=Il,t.stateNode=l,l._reactInternals=t,Zs(t,r,e,n),t=to(null,t,r,!0,s,n)):(t.tag=0,b&&s&&Do(t),pe(null,t,l,n),t=t.child),t;case 16:r=t.elementType;e:{switch(Xr(e,t),e=t.pendingProps,l=r._init,r=l(r._payload),t.type=r,l=t.tag=Pp(r),e=$e(r,e),l){case 0:t=eo(null,t,r,e,n);break e;case 1:t=fa(null,t,r,e,n);break e;case 11:t=ca(null,t,r,e,n);break e;case 14:t=da(null,t,r,$e(r.type,e),n);break e}throw Error(_(306,r,""))}return t;case 0:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:$e(r,l),eo(e,t,r,l,n);case 1:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:$e(r,l),fa(e,t,r,l,n);case 3:e:{if(_c(t),e===null)throw Error(_(387));r=t.pendingProps,s=t.memoizedState,l=s.element,Zu(e,t),hl(t,r,null,n);var o=t.memoizedState;if(r=o.element,s.isDehydrated)if(s={element:r,isDehydrated:!1,cache:o.cache,pendingSuspenseBoundaries:o.pendingSuspenseBoundaries,transitions:o.transitions},t.updateQueue.baseState=s,t.memoizedState=s,t.flags&256){l=xn(Error(_(423)),t),t=pa(e,t,r,n,l);break e}else if(r!==l){l=xn(Error(_(424)),t),t=pa(e,t,r,n,l);break e}else for(_e=ht(t.stateNode.containerInfo.firstChild),Ee=t,b=!0,Fe=null,n=Yu(t,null,r,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(hn(),r===l){t=nt(e,t,n);break e}pe(e,t,r,n)}t=t.child}return t;case 5:return qu(t),e===null&&Xs(t),r=t.type,l=t.pendingProps,s=e!==null?e.memoizedProps:null,o=l.children,Hs(r,l)?o=null:s!==null&&Hs(r,s)&&(t.flags|=32),Nc(e,t),pe(e,t,o,n),t.child;case 6:return e===null&&Xs(t),null;case 13:return Ec(e,t,n);case 4:return Qo(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=vn(t,null,r,n):pe(e,t,r,n),t.child;case 11:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:$e(r,l),ca(e,t,r,l,n);case 7:return pe(e,t,t.pendingProps,n),t.child;case 8:return pe(e,t,t.pendingProps.children,n),t.child;case 12:return pe(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,l=t.pendingProps,s=t.memoizedProps,o=l.value,A(pl,r._currentValue),r._currentValue=o,s!==null)if(Ve(s.value,o)){if(s.children===l.children&&!ke.current){t=nt(e,t,n);break e}}else for(s=t.child,s!==null&&(s.return=t);s!==null;){var i=s.dependencies;if(i!==null){o=s.child;for(var a=i.firstContext;a!==null;){if(a.context===r){if(s.tag===1){a=Ze(-1,n&-n),a.tag=2;var c=s.updateQueue;if(c!==null){c=c.shared;var h=c.pending;h===null?a.next=a:(a.next=h.next,h.next=a),c.pending=a}}s.lanes|=n,a=s.alternate,a!==null&&(a.lanes|=n),Ys(s.return,n,t),i.lanes|=n;break}a=a.next}}else if(s.tag===10)o=s.type===t.type?null:s.child;else if(s.tag===18){if(o=s.return,o===null)throw Error(_(341));o.lanes|=n,i=o.alternate,i!==null&&(i.lanes|=n),Ys(o,n,t),o=s.sibling}else o=s.child;if(o!==null)o.return=s;else for(o=s;o!==null;){if(o===t){o=null;break}if(s=o.sibling,s!==null){s.return=o.return,o=s;break}o=o.return}s=o}pe(e,t,l.children,n),t=t.child}return t;case 9:return l=t.type,r=t.pendingProps.children,dn(t,n),l=Ie(l),r=r(l),t.flags|=1,pe(e,t,r,n),t.child;case 14:return r=t.type,l=$e(r,t.pendingProps),l=$e(r.type,l),da(e,t,r,l,n);case 15:return Sc(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:$e(r,l),Xr(e,t),t.tag=1,we(r)?(e=!0,cl(t)):e=!1,dn(t,n),yc(t,r,l),Zs(t,r,l,n),to(null,t,r,!0,e,n);case 19:return Cc(e,t,n);case 22:return jc(e,t,n)}throw Error(_(156,t.tag))};function Wc(e,t){return vu(e,t)}function Cp(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function ze(e,t,n,r){return new Cp(e,t,n,r)}function ii(e){return e=e.prototype,!(!e||!e.isReactComponent)}function Pp(e){if(typeof e=="function")return ii(e)?1:0;if(e!=null){if(e=e.$$typeof,e===Eo)return 11;if(e===Co)return 14}return 2}function yt(e,t){var n=e.alternate;return n===null?(n=ze(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function Zr(e,t,n,r,l,s){var o=2;if(r=e,typeof e=="function")ii(e)&&(o=1);else if(typeof e=="string")o=5;else e:switch(e){case Xt:return Mt(n.children,l,s,t);case _o:o=8,l|=8;break;case Ss:return e=ze(12,n,t,l|2),e.elementType=Ss,e.lanes=s,e;case js:return e=ze(13,n,t,l),e.elementType=js,e.lanes=s,e;case Ns:return e=ze(19,n,t,l),e.elementType=Ns,e.lanes=s,e;case qa:return $l(n,l,s,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case Ja:o=10;break e;case Za:o=9;break e;case Eo:o=11;break e;case Co:o=14;break e;case st:o=16,r=null;break e}throw Error(_(130,e==null?e:typeof e,""))}return t=ze(o,n,t,l),t.elementType=e,t.type=r,t.lanes=s,t}function Mt(e,t,n,r){return e=ze(7,e,r,t),e.lanes=n,e}function $l(e,t,n,r){return e=ze(22,e,r,t),e.elementType=qa,e.lanes=n,e.stateNode={isHidden:!1},e}function hs(e,t,n){return e=ze(6,e,null,t),e.lanes=n,e}function vs(e,t,n){return t=ze(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function Lp(e,t,n,r,l){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=Yl(0),this.expirationTimes=Yl(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=Yl(0),this.identifierPrefix=r,this.onRecoverableError=l,this.mutableSourceEagerHydrationData=null}function ai(e,t,n,r,l,s,o,i,a){return e=new Lp(e,t,n,i,a),t===1?(t=1,s===!0&&(t|=8)):t=0,s=ze(3,null,null,t),e.current=s,s.stateNode=e,s.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},bo(s),e}function Rp(e,t,n){var r=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(Kc)}catch(e){console.error(e)}}Kc(),Ka.exports=Pe;var Up=Ka.exports,_a=Up;ks.createRoot=_a.createRoot,ks.hydrateRoot=_a.hydrateRoot;/** + * @remix-run/router v1.23.2 + * + * Copyright (c) Remix Software Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE.md file in the root directory of this source tree. + * + * @license MIT + */function dr(){return dr=Object.assign?Object.assign.bind():function(e){for(var t=1;t"u")throw new Error(t)}function fi(e,t){if(!e){typeof console<"u"&&console.warn(t);try{throw new Error(t)}catch{}}}function $p(){return Math.random().toString(36).substr(2,8)}function Ca(e,t){return{usr:e.state,key:e.key,idx:t}}function mo(e,t,n,r){return n===void 0&&(n=null),dr({pathname:typeof e=="string"?e:e.pathname,search:"",hash:""},typeof t=="string"?Nn(t):t,{state:n,key:t&&t.key||r||$p()})}function Nl(e){let{pathname:t="/",search:n="",hash:r=""}=e;return n&&n!=="?"&&(t+=n.charAt(0)==="?"?n:"?"+n),r&&r!=="#"&&(t+=r.charAt(0)==="#"?r:"#"+r),t}function Nn(e){let t={};if(e){let n=e.indexOf("#");n>=0&&(t.hash=e.substr(n),e=e.substr(0,n));let r=e.indexOf("?");r>=0&&(t.search=e.substr(r),e=e.substr(0,r)),e&&(t.pathname=e)}return t}function Dp(e,t,n,r){r===void 0&&(r={});let{window:l=document.defaultView,v5Compat:s=!1}=r,o=l.history,i=dt.Pop,a=null,c=h();c==null&&(c=0,o.replaceState(dr({},o.state,{idx:c}),""));function h(){return(o.state||{idx:null}).idx}function m(){i=dt.Pop;let C=h(),f=C==null?null:C-c;c=C,a&&a({action:i,location:j.location,delta:f})}function v(C,f){i=dt.Push;let d=mo(j.location,C,f);c=h()+1;let p=Ca(d,c),x=j.createHref(d);try{o.pushState(p,"",x)}catch(E){if(E instanceof DOMException&&E.name==="DataCloneError")throw E;l.location.assign(x)}s&&a&&a({action:i,location:j.location,delta:1})}function g(C,f){i=dt.Replace;let d=mo(j.location,C,f);c=h();let p=Ca(d,c),x=j.createHref(d);o.replaceState(p,"",x),s&&a&&a({action:i,location:j.location,delta:0})}function y(C){let f=l.location.origin!=="null"?l.location.origin:l.location.href,d=typeof C=="string"?C:Nl(C);return d=d.replace(/ $/,"%20"),q(f,"No window.location.(origin|href) available to create URL for href: "+d),new URL(d,f)}let j={get action(){return i},get location(){return e(l,o)},listen(C){if(a)throw new Error("A history only accepts one active listener");return l.addEventListener(Ea,m),a=C,()=>{l.removeEventListener(Ea,m),a=null}},createHref(C){return t(l,C)},createURL:y,encodeLocation(C){let f=y(C);return{pathname:f.pathname,search:f.search,hash:f.hash}},push:v,replace:g,go(C){return o.go(C)}};return j}var Pa;(function(e){e.data="data",e.deferred="deferred",e.redirect="redirect",e.error="error"})(Pa||(Pa={}));function Fp(e,t,n){return n===void 0&&(n="/"),Bp(e,t,n)}function Bp(e,t,n,r){let l=typeof t=="string"?Nn(t):t,s=pi(l.pathname||"/",n);if(s==null)return null;let o=Gc(e);Ap(o);let i=null;for(let a=0;i==null&&a{let a={relativePath:i===void 0?s.path||"":i,caseSensitive:s.caseSensitive===!0,childrenIndex:o,route:s};a.relativePath.startsWith("/")&&(q(a.relativePath.startsWith(r),'Absolute route path "'+a.relativePath+'" nested under path '+('"'+r+'" is not valid. An absolute child route path ')+"must start with the combined path of all its parent routes."),a.relativePath=a.relativePath.slice(r.length));let c=kt([r,a.relativePath]),h=n.concat(a);s.children&&s.children.length>0&&(q(s.index!==!0,"Index routes must not have child routes. Please remove "+('all child routes from route path "'+c+'".')),Gc(s.children,t,h,c)),!(s.path==null&&!s.index)&&t.push({path:c,score:Gp(c,s.index),routesMeta:h})};return e.forEach((s,o)=>{var i;if(s.path===""||!((i=s.path)!=null&&i.includes("?")))l(s,o);else for(let a of Xc(s.path))l(s,o,a)}),t}function Xc(e){let t=e.split("/");if(t.length===0)return[];let[n,...r]=t,l=n.endsWith("?"),s=n.replace(/\?$/,"");if(r.length===0)return l?[s,""]:[s];let o=Xc(r.join("/")),i=[];return i.push(...o.map(a=>a===""?s:[s,a].join("/"))),l&&i.push(...o),i.map(a=>e.startsWith("/")&&a===""?"/":a)}function Ap(e){e.sort((t,n)=>t.score!==n.score?n.score-t.score:Xp(t.routesMeta.map(r=>r.childrenIndex),n.routesMeta.map(r=>r.childrenIndex)))}const Vp=/^:[\w-]+$/,Wp=3,Hp=2,bp=1,Qp=10,Kp=-2,La=e=>e==="*";function Gp(e,t){let n=e.split("/"),r=n.length;return n.some(La)&&(r+=Kp),t&&(r+=Hp),n.filter(l=>!La(l)).reduce((l,s)=>l+(Vp.test(s)?Wp:s===""?bp:Qp),r)}function Xp(e,t){return e.length===t.length&&e.slice(0,-1).every((r,l)=>r===t[l])?e[e.length-1]-t[t.length-1]:0}function Yp(e,t,n){let{routesMeta:r}=e,l={},s="/",o=[];for(let i=0;i{let{paramName:v,isOptional:g}=h;if(v==="*"){let j=i[m]||"";o=s.slice(0,s.length-j.length).replace(/(.)\/+$/,"$1")}const y=i[m];return g&&!y?c[v]=void 0:c[v]=(y||"").replace(/%2F/g,"/"),c},{}),pathname:s,pathnameBase:o,pattern:e}}function Zp(e,t,n){t===void 0&&(t=!1),n===void 0&&(n=!0),fi(e==="*"||!e.endsWith("*")||e.endsWith("/*"),'Route path "'+e+'" will be treated as if it were '+('"'+e.replace(/\*$/,"/*")+'" because the `*` character must ')+"always follow a `/` in the pattern. To get rid of this warning, "+('please change the route path to "'+e.replace(/\*$/,"/*")+'".'));let r=[],l="^"+e.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(o,i,a)=>(r.push({paramName:i,isOptional:a!=null}),a?"/?([^\\/]+)?":"/([^\\/]+)"));return e.endsWith("*")?(r.push({paramName:"*"}),l+=e==="*"||e==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):n?l+="\\/*$":e!==""&&e!=="/"&&(l+="(?:(?=\\/|$))"),[new RegExp(l,t?void 0:"i"),r]}function qp(e){try{return e.split("/").map(t=>decodeURIComponent(t).replace(/\//g,"%2F")).join("/")}catch(t){return fi(!1,'The URL path "'+e+'" could not be decoded because it is is a malformed URL segment. This is probably due to a bad percent '+("encoding ("+t+").")),e}}function pi(e,t){if(t==="/")return e;if(!e.toLowerCase().startsWith(t.toLowerCase()))return null;let n=t.endsWith("/")?t.length-1:t.length,r=e.charAt(n);return r&&r!=="/"?null:e.slice(n)||"/"}const em=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,tm=e=>em.test(e);function nm(e,t){t===void 0&&(t="/");let{pathname:n,search:r="",hash:l=""}=typeof e=="string"?Nn(e):e,s;if(n)if(tm(n))s=n;else{if(n.includes("//")){let o=n;n=n.replace(/\/\/+/g,"/"),fi(!1,"Pathnames cannot have embedded double slashes - normalizing "+(o+" -> "+n))}n.startsWith("/")?s=Ra(n.substring(1),"/"):s=Ra(n,t)}else s=t;return{pathname:s,search:sm(r),hash:om(l)}}function Ra(e,t){let n=t.replace(/\/+$/,"").split("/");return e.split("/").forEach(l=>{l===".."?n.length>1&&n.pop():l!=="."&&n.push(l)}),n.length>1?n.join("/"):"/"}function gs(e,t,n,r){return"Cannot include a '"+e+"' character in a manually specified "+("`to."+t+"` field ["+JSON.stringify(r)+"]. Please separate it out to the ")+("`to."+n+"` field. Alternatively you may provide the full path as ")+'a string in and the router will parse it for you.'}function rm(e){return e.filter((t,n)=>n===0||t.route.path&&t.route.path.length>0)}function Yc(e,t){let n=rm(e);return t?n.map((r,l)=>l===n.length-1?r.pathname:r.pathnameBase):n.map(r=>r.pathnameBase)}function Jc(e,t,n,r){r===void 0&&(r=!1);let l;typeof e=="string"?l=Nn(e):(l=dr({},e),q(!l.pathname||!l.pathname.includes("?"),gs("?","pathname","search",l)),q(!l.pathname||!l.pathname.includes("#"),gs("#","pathname","hash",l)),q(!l.search||!l.search.includes("#"),gs("#","search","hash",l)));let s=e===""||l.pathname==="",o=s?"/":l.pathname,i;if(o==null)i=n;else{let m=t.length-1;if(!r&&o.startsWith("..")){let v=o.split("/");for(;v[0]==="..";)v.shift(),m-=1;l.pathname=v.join("/")}i=m>=0?t[m]:"/"}let a=nm(l,i),c=o&&o!=="/"&&o.endsWith("/"),h=(s||o===".")&&n.endsWith("/");return!a.pathname.endsWith("/")&&(c||h)&&(a.pathname+="/"),a}const kt=e=>e.join("/").replace(/\/\/+/g,"/"),lm=e=>e.replace(/\/+$/,"").replace(/^\/*/,"/"),sm=e=>!e||e==="?"?"":e.startsWith("?")?e:"?"+e,om=e=>!e||e==="#"?"":e.startsWith("#")?e:"#"+e;function im(e){return e!=null&&typeof e.status=="number"&&typeof e.statusText=="string"&&typeof e.internal=="boolean"&&"data"in e}const Zc=["post","put","patch","delete"];new Set(Zc);const am=["get",...Zc];new Set(am);/** + * React Router v6.30.3 + * + * Copyright (c) Remix Software Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE.md file in the root directory of this source tree. + * + * @license MIT + */function fr(){return fr=Object.assign?Object.assign.bind():function(e){for(var t=1;t{i.current=!0}),k.useCallback(function(c,h){if(h===void 0&&(h={}),!i.current)return;if(typeof c=="number"){r.go(c);return}let m=Jc(c,JSON.parse(o),s,h.relative==="path");e==null&&t!=="/"&&(m.pathname=m.pathname==="/"?t:kt([t,m.pathname])),(h.replace?r.replace:r.push)(m,h.state,h)},[t,r,o,s,e])}function td(e,t){let{relative:n}=t===void 0?{}:t,{future:r}=k.useContext(bt),{matches:l}=k.useContext(Qt),{pathname:s}=yr(),o=JSON.stringify(Yc(l,r.v7_relativeSplatPath));return k.useMemo(()=>Jc(e,JSON.parse(o),s,n==="path"),[e,o,s,n])}function fm(e,t){return pm(e,t)}function pm(e,t,n,r){xr()||q(!1);let{navigator:l}=k.useContext(bt),{matches:s}=k.useContext(Qt),o=s[s.length-1],i=o?o.params:{};o&&o.pathname;let a=o?o.pathnameBase:"/";o&&o.route;let c=yr(),h;if(t){var m;let C=typeof t=="string"?Nn(t):t;a==="/"||(m=C.pathname)!=null&&m.startsWith(a)||q(!1),h=C}else h=c;let v=h.pathname||"/",g=v;if(a!=="/"){let C=a.replace(/^\//,"").split("/");g="/"+v.replace(/^\//,"").split("/").slice(C.length).join("/")}let y=Fp(e,{pathname:g}),j=xm(y&&y.map(C=>Object.assign({},C,{params:Object.assign({},i,C.params),pathname:kt([a,l.encodeLocation?l.encodeLocation(C.pathname).pathname:C.pathname]),pathnameBase:C.pathnameBase==="/"?a:kt([a,l.encodeLocation?l.encodeLocation(C.pathnameBase).pathname:C.pathnameBase])})),s,n,r);return t&&j?k.createElement(Vl.Provider,{value:{location:fr({pathname:"/",search:"",hash:"",state:null,key:"default"},h),navigationType:dt.Pop}},j):j}function mm(){let e=Sm(),t=im(e)?e.status+" "+e.statusText:e instanceof Error?e.message:JSON.stringify(e),n=e instanceof Error?e.stack:null,l={padding:"0.5rem",backgroundColor:"rgba(200,200,200, 0.5)"};return k.createElement(k.Fragment,null,k.createElement("h2",null,"Unexpected Application Error!"),k.createElement("h3",{style:{fontStyle:"italic"}},t),n?k.createElement("pre",{style:l},n):null,null)}const hm=k.createElement(mm,null);class vm extends k.Component{constructor(t){super(t),this.state={location:t.location,revalidation:t.revalidation,error:t.error}}static getDerivedStateFromError(t){return{error:t}}static getDerivedStateFromProps(t,n){return n.location!==t.location||n.revalidation!=="idle"&&t.revalidation==="idle"?{error:t.error,location:t.location,revalidation:t.revalidation}:{error:t.error!==void 0?t.error:n.error,location:n.location,revalidation:t.revalidation||n.revalidation}}componentDidCatch(t,n){console.error("React Router caught the following error during render",t,n)}render(){return this.state.error!==void 0?k.createElement(Qt.Provider,{value:this.props.routeContext},k.createElement(qc.Provider,{value:this.state.error,children:this.props.component})):this.props.children}}function gm(e){let{routeContext:t,match:n,children:r}=e,l=k.useContext(mi);return l&&l.static&&l.staticContext&&(n.route.errorElement||n.route.ErrorBoundary)&&(l.staticContext._deepestRenderedBoundaryId=n.route.id),k.createElement(Qt.Provider,{value:t},r)}function xm(e,t,n,r){var l;if(t===void 0&&(t=[]),n===void 0&&(n=null),r===void 0&&(r=null),e==null){var s;if(!n)return null;if(n.errors)e=n.matches;else if((s=r)!=null&&s.v7_partialHydration&&t.length===0&&!n.initialized&&n.matches.length>0)e=n.matches;else return null}let o=e,i=(l=n)==null?void 0:l.errors;if(i!=null){let h=o.findIndex(m=>m.route.id&&(i==null?void 0:i[m.route.id])!==void 0);h>=0||q(!1),o=o.slice(0,Math.min(o.length,h+1))}let a=!1,c=-1;if(n&&r&&r.v7_partialHydration)for(let h=0;h=0?o=o.slice(0,c+1):o=[o[0]];break}}}return o.reduceRight((h,m,v)=>{let g,y=!1,j=null,C=null;n&&(g=i&&m.route.id?i[m.route.id]:void 0,j=m.route.errorElement||hm,a&&(c<0&&v===0?(Nm("route-fallback"),y=!0,C=null):c===v&&(y=!0,C=m.route.hydrateFallbackElement||null)));let f=t.concat(o.slice(0,v+1)),d=()=>{let p;return g?p=j:y?p=C:m.route.Component?p=k.createElement(m.route.Component,null):m.route.element?p=m.route.element:p=h,k.createElement(gm,{match:m,routeContext:{outlet:h,matches:f,isDataRoute:n!=null},children:p})};return n&&(m.route.ErrorBoundary||m.route.errorElement||v===0)?k.createElement(vm,{location:n.location,revalidation:n.revalidation,component:j,error:g,children:d(),routeContext:{outlet:null,matches:f,isDataRoute:!0}}):d()},null)}var nd=function(e){return e.UseBlocker="useBlocker",e.UseRevalidator="useRevalidator",e.UseNavigateStable="useNavigate",e}(nd||{}),rd=function(e){return e.UseBlocker="useBlocker",e.UseLoaderData="useLoaderData",e.UseActionData="useActionData",e.UseRouteError="useRouteError",e.UseNavigation="useNavigation",e.UseRouteLoaderData="useRouteLoaderData",e.UseMatches="useMatches",e.UseRevalidator="useRevalidator",e.UseNavigateStable="useNavigate",e.UseRouteId="useRouteId",e}(rd||{});function ym(e){let t=k.useContext(mi);return t||q(!1),t}function km(e){let t=k.useContext(um);return t||q(!1),t}function wm(e){let t=k.useContext(Qt);return t||q(!1),t}function ld(e){let t=wm(),n=t.matches[t.matches.length-1];return n.route.id||q(!1),n.route.id}function Sm(){var e;let t=k.useContext(qc),n=km(),r=ld();return t!==void 0?t:(e=n.errors)==null?void 0:e[r]}function jm(){let{router:e}=ym(nd.UseNavigateStable),t=ld(rd.UseNavigateStable),n=k.useRef(!1);return ed(()=>{n.current=!0}),k.useCallback(function(l,s){s===void 0&&(s={}),n.current&&(typeof l=="number"?e.navigate(l):e.navigate(l,fr({fromRouteId:t},s)))},[e,t])}const Ta={};function Nm(e,t,n){Ta[e]||(Ta[e]=!0)}function _m(e,t){e==null||e.v7_startTransition,e==null||e.v7_relativeSplatPath}function qr(e){q(!1)}function Em(e){let{basename:t="/",children:n=null,location:r,navigationType:l=dt.Pop,navigator:s,static:o=!1,future:i}=e;xr()&&q(!1);let a=t.replace(/^\/*/,"/"),c=k.useMemo(()=>({basename:a,navigator:s,static:o,future:fr({v7_relativeSplatPath:!1},i)}),[a,i,s,o]);typeof r=="string"&&(r=Nn(r));let{pathname:h="/",search:m="",hash:v="",state:g=null,key:y="default"}=r,j=k.useMemo(()=>{let C=pi(h,a);return C==null?null:{location:{pathname:C,search:m,hash:v,state:g,key:y},navigationType:l}},[a,h,m,v,g,y,l]);return j==null?null:k.createElement(bt.Provider,{value:c},k.createElement(Vl.Provider,{children:n,value:j}))}function Cm(e){let{children:t,location:n}=e;return fm(ho(t),n)}new Promise(()=>{});function ho(e,t){t===void 0&&(t=[]);let n=[];return k.Children.forEach(e,(r,l)=>{if(!k.isValidElement(r))return;let s=[...t,l];if(r.type===k.Fragment){n.push.apply(n,ho(r.props.children,s));return}r.type!==qr&&q(!1),!r.props.index||!r.props.children||q(!1);let o={id:r.props.id||s.join("-"),caseSensitive:r.props.caseSensitive,element:r.props.element,Component:r.props.Component,index:r.props.index,path:r.props.path,loader:r.props.loader,action:r.props.action,errorElement:r.props.errorElement,ErrorBoundary:r.props.ErrorBoundary,hasErrorBoundary:r.props.ErrorBoundary!=null||r.props.errorElement!=null,shouldRevalidate:r.props.shouldRevalidate,handle:r.props.handle,lazy:r.props.lazy};r.props.children&&(o.children=ho(r.props.children,s)),n.push(o)}),n}/** + * React Router DOM v6.30.3 + * + * Copyright (c) Remix Software Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE.md file in the root directory of this source tree. + * + * @license MIT + */function vo(){return vo=Object.assign?Object.assign.bind():function(e){for(var t=1;t=0)&&(n[l]=e[l]);return n}function Lm(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function Rm(e,t){return e.button===0&&(!t||t==="_self")&&!Lm(e)}function go(e){return e===void 0&&(e=""),new URLSearchParams(typeof e=="string"||Array.isArray(e)||e instanceof URLSearchParams?e:Object.keys(e).reduce((t,n)=>{let r=e[n];return t.concat(Array.isArray(r)?r.map(l=>[n,l]):[[n,r]])},[]))}function Tm(e,t){let n=go(e);return t&&t.forEach((r,l)=>{n.has(l)||t.getAll(l).forEach(s=>{n.append(l,s)})}),n}const zm=["onClick","relative","reloadDocument","replace","state","target","to","preventScrollReset","viewTransition"],Om="6";try{window.__reactRouterVersion=Om}catch{}const Im="startTransition",za=jd[Im];function Um(e){let{basename:t,children:n,future:r,window:l}=e,s=k.useRef();s.current==null&&(s.current=Mp({window:l,v5Compat:!0}));let o=s.current,[i,a]=k.useState({action:o.action,location:o.location}),{v7_startTransition:c}=r||{},h=k.useCallback(m=>{c&&za?za(()=>a(m)):a(m)},[a,c]);return k.useLayoutEffect(()=>o.listen(h),[o,h]),k.useEffect(()=>_m(r),[r]),k.createElement(Em,{basename:t,children:n,location:i.location,navigationType:i.action,navigator:o,future:r})}const Mm=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u",$m=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,Oa=k.forwardRef(function(t,n){let{onClick:r,relative:l,reloadDocument:s,replace:o,state:i,target:a,to:c,preventScrollReset:h,viewTransition:m}=t,v=Pm(t,zm),{basename:g}=k.useContext(bt),y,j=!1;if(typeof c=="string"&&$m.test(c)&&(y=c,Mm))try{let p=new URL(window.location.href),x=c.startsWith("//")?new URL(p.protocol+c):new URL(c),E=pi(x.pathname,g);x.origin===p.origin&&E!=null?c=E+x.search+x.hash:j=!0}catch{}let C=cm(c,{relative:l}),f=Dm(c,{replace:o,state:i,target:a,preventScrollReset:h,relative:l,viewTransition:m});function d(p){r&&r(p),p.defaultPrevented||f(p)}return k.createElement("a",vo({},v,{href:y||C,onClick:j||s?r:d,ref:n,target:a}))});var Ia;(function(e){e.UseScrollRestoration="useScrollRestoration",e.UseSubmit="useSubmit",e.UseSubmitFetcher="useSubmitFetcher",e.UseFetcher="useFetcher",e.useViewTransitionState="useViewTransitionState"})(Ia||(Ia={}));var Ua;(function(e){e.UseFetcher="useFetcher",e.UseFetchers="useFetchers",e.UseScrollRestoration="useScrollRestoration"})(Ua||(Ua={}));function Dm(e,t){let{target:n,replace:r,state:l,preventScrollReset:s,relative:o,viewTransition:i}=t===void 0?{}:t,a=kr(),c=yr(),h=td(e,{relative:o});return k.useCallback(m=>{if(Rm(m,n)){m.preventDefault();let v=r!==void 0?r:Nl(c)===Nl(h);a(e,{replace:v,state:l,preventScrollReset:s,relative:o,viewTransition:i})}},[c,a,h,r,l,n,e,s,o,i])}function hi(e){let t=k.useRef(go(e)),n=k.useRef(!1),r=yr(),l=k.useMemo(()=>Tm(r.search,n.current?null:t.current),[r.search]),s=kr(),o=k.useCallback((i,a)=>{const c=go(typeof i=="function"?i(l):i);n.current=!0,s("?"+c,a)},[s,l]);return[l,o]}const me="transition active:brightness-95 active:scale-[0.99] focus:outline-none focus-visible:ring-2 focus-visible:ring-slate-400/60 dark:focus-visible:ring-slate-500/50",wr="rounded-xl px-3 py-2 text-sm border bg-white text-slate-900 border-slate-300 placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-slate-300 dark:bg-slate-950/60 dark:text-slate-100 dark:border-slate-700 dark:placeholder:text-slate-500 dark:focus:ring-slate-700",Vt=wr,de="rounded-2xl border bg-white/80 border-slate-200 shadow-sm dark:bg-slate-900/40 dark:border-slate-800",sd=[{value:"plain-lines",label:"Построчно"},{value:"single-line",label:"Одной строкой"},{value:"keenetic-ip",label:"Keenetic (.bat IPv4 маршруты)"},{value:"json",label:"JSON"}],Fm={runetfreedom:"bg-rose-100 text-rose-700 dark:bg-rose-900/40 dark:text-rose-300",loyalsoldier:"bg-violet-100 text-violet-700 dark:bg-violet-900/40 dark:text-violet-300",daniellavrushin:"bg-amber-100 text-amber-700 dark:bg-amber-900/40 dark:text-amber-300",v2fly:"bg-sky-100 text-sky-700 dark:bg-sky-900/40 dark:text-sky-300"},Bm="bg-slate-100 text-slate-600 dark:bg-slate-800 dark:text-slate-400";function Am(){const[e,t]=k.useState(()=>{var r,l;const n=localStorage.getItem("theme");return n==="dark"?!0:n==="light"?!1:((l=(r=window.matchMedia)==null?void 0:r.call(window,"(prefers-color-scheme: dark)"))==null?void 0:l.matches)??!0});return k.useEffect(()=>{document.documentElement.classList.toggle("dark",e),localStorage.setItem("theme",e?"dark":"light")},[e]),u.jsx("button",{onClick:()=>t(n=>!n),className:`text-xs px-3 py-1 rounded-xl border bg-white text-slate-900 border-slate-200 hover:bg-slate-50 dark:bg-slate-800 dark:text-slate-200 dark:border-slate-700 dark:hover:bg-slate-700 ${me}`,type:"button",children:e?"☀ Light":"☾ Dark"})}const Vm=[{id:"popular",label:"Популярные"},{id:"search",label:"Поиск"},{id:"lookup",label:"DNS поиск"},{id:"routing",label:"Для приложений"}];function vi({back:e,tab:t,setTab:n}){return u.jsxs("div",{className:"mb-4",children:[u.jsxs("div",{className:"flex items-center justify-between mb-3",children:[u.jsx("div",{className:"flex items-center gap-3",children:e?u.jsxs(Oa,{to:e.to,className:"text-sm underline text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-white",children:["← ",e.label]}):u.jsx("div",{className:"text-xl font-bold tracking-tight",children:"GeoExport"})}),u.jsxs("div",{className:"flex items-center gap-2",children:[!e&&u.jsx(Oa,{to:"/sources",className:"text-xs px-3 py-1.5 rounded-lg hover:bg-slate-200 dark:hover:bg-slate-800 text-slate-600 dark:text-slate-400",children:"Источники"}),u.jsx(Am,{})]})]}),t&&n&&u.jsx("div",{className:"flex gap-1 overflow-x-auto pb-1 -mx-4 px-4 scrollbar-hide",children:Vm.map(r=>u.jsx("button",{onClick:()=>n(r.id),className:`text-xs px-3 py-2 rounded-lg transition-colors whitespace-nowrap shrink-0 ${t===r.id?"bg-slate-900 text-white dark:bg-slate-100 dark:text-slate-900":"bg-slate-100 dark:bg-slate-800 text-slate-600 dark:text-slate-400 hover:bg-slate-200 dark:hover:bg-slate-700"}`,type:"button",children:r.label},r.id))})]})}function kn(e){const[t,n]=e.split(":",2),r=n.replace(/^geoip_|^geosite_/,"");return`${t}:${r}`}function _l({text:e}){const[t,n]=Ne.useState(!1),r=l=>{l.stopPropagation(),navigator.clipboard.writeText(e),n(!0),setTimeout(()=>n(!1),1500)};return u.jsx("button",{onClick:r,title:"Скопировать",className:`text-xs px-2 py-0.5 rounded border border-slate-200 dark:border-slate-700 hover:bg-slate-100 dark:hover:bg-slate-700 font-mono transition-colors ${me}`,type:"button",children:t?"✓":"⎘"})}function Wm({type:e}){return u.jsx("span",{className:`text-xs px-2 py-0.5 rounded-full font-mono shrink-0 ${e==="geoip"?"bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300":"bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-300"}`,children:e})}function Wl({slug:e,name:t}){const n=Fm[e]??Bm;return u.jsx("span",{className:`text-xs px-2 py-0.5 rounded-full shrink-0 ${n}`,children:t})}function Hm({domain:e,name:t}){const[n,r]=Ne.useState(!1);return!e||n?u.jsx("div",{className:"w-7 h-7 rounded-lg bg-slate-200 dark:bg-slate-700 flex items-center justify-center text-xs font-bold text-slate-500 dark:text-slate-400 shrink-0",children:t[0]}):u.jsx("img",{src:`https://www.google.com/s2/favicons?domain=${e}&sz=64`,alt:t,className:"w-7 h-7 rounded-lg object-contain bg-white dark:bg-slate-800 p-0.5 shrink-0",onError:()=>r(!0)})}function bm({svc:e,sources:t,sourceFilter:n}){const[r,l]=Ne.useState(!1),[s,o]=Ne.useState(""),[i,a]=Ne.useState(!1),[c,h]=Ne.useState("plain-lines"),[m,v]=Ne.useState("domains,ip4,ip6"),[g,y]=Ne.useState(!1),j=kr(),C=Ne.useMemo(()=>{const w=n==="all"?e.categories:e.categories.filter(S=>S.source_slug===n);return w.length>0?w:e.categories},[e.categories,n]),f=C.length>0,d=[...new Set(C.map(w=>w.source_slug))],p=async()=>{if(f){a(!0);try{const w=new URLSearchParams;C.forEach(z=>w.append("category",z.full_name)),w.set("type",m),w.set("format",c==="single-line"?"plain-lines":c),n!=="all"&&w.set("source",n);let P=await(await fetch(`/api/export?${w}`)).text();c==="single-line"&&(P=P.trim().split(/\r?\n/).join(",")),o(P)}finally{a(!1)}}};k.useEffect(()=>{r&&p()},[r,c,m,n]);const x=()=>{const w=new Blob([s],{type:"text/plain;charset=utf-8"}),S=URL.createObjectURL(w),P=document.createElement("a");P.href=S,P.download=`${e.name.replace(/[^a-z0-9]/gi,"_").toLowerCase()}.txt`,P.click(),URL.revokeObjectURL(S)},E=()=>{navigator.clipboard.writeText(s),y(!0),setTimeout(()=>y(!1),1500)};return u.jsxs("div",{className:`${de} overflow-hidden`,children:[u.jsxs("button",{onClick:()=>f&&l(w=>!w),disabled:!f,className:`w-full flex items-center gap-3 px-4 py-3 text-left ${f?"hover:bg-white dark:hover:bg-slate-900/70 "+me:"opacity-40 cursor-not-allowed"}`,type:"button",children:[u.jsx(Hm,{domain:e.domain,name:e.name}),u.jsxs("div",{className:"flex-1 min-w-0",children:[u.jsx("div",{className:"text-sm font-medium text-slate-900 dark:text-slate-100",children:e.name}),f?u.jsxs("div",{className:"flex items-center gap-1 flex-wrap mt-0.5",children:[u.jsxs("span",{className:"text-xs text-slate-400",children:[C.length," ",C.length===1?"категория":C.length<5?"категории":"категорий"]}),d.map(w=>{var P;const S=t.find(z=>z.slug===w);return u.jsx(Wl,{slug:w,name:((P=S==null?void 0:S.name)==null?void 0:P.split(" ")[0])??w},w)})]}):u.jsx("div",{className:"text-xs text-slate-400",children:"Нет в базах данных"})]}),f&&u.jsx("svg",{className:`w-4 h-4 text-slate-400 transition-transform shrink-0 ${r?"rotate-180":""}`,viewBox:"0 0 20 20",fill:"currentColor",children:u.jsx("path",{fillRule:"evenodd",d:"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z",clipRule:"evenodd"})})]}),r&&u.jsxs("div",{className:"border-t border-slate-100 dark:border-slate-800 px-4 py-3 space-y-3",children:[u.jsx("div",{className:"flex flex-wrap gap-1",children:C.map(w=>u.jsxs("span",{className:`inline-flex items-center gap-1 text-xs px-2 py-0.5 rounded-full font-mono ${w.type==="geoip"?"bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300":"bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-300"}`,children:[kn(w.full_name),u.jsx(_l,{text:kn(w.full_name)})]},w.full_name+w.source_slug))}),u.jsxs("div",{className:"flex gap-2 flex-wrap",children:[u.jsxs("select",{value:m,onChange:w=>v(w.target.value),className:`text-xs ${Vt}`,children:[u.jsx("option",{value:"domains,ip4,ip6",children:"Домены + IPv4 + IPv6"}),u.jsx("option",{value:"domains,ip4",children:"Домены + IPv4"}),u.jsx("option",{value:"ip4,ip6",children:"IPv4 + IPv6"}),u.jsx("option",{value:"domains",children:"Только домены"}),u.jsx("option",{value:"ip4",children:"Только IPv4"}),u.jsx("option",{value:"ip6",children:"Только IPv6"})]}),u.jsx("select",{value:c,onChange:w=>h(w.target.value),className:`text-xs ${Vt}`,children:sd.map(w=>u.jsx("option",{value:w.value,children:w.label},w.value))})]}),u.jsx("textarea",{readOnly:!0,value:i?"Загрузка…":s,className:"w-full h-28 rounded-xl px-3 py-2 text-xs border bg-white text-slate-900 border-slate-200 dark:bg-slate-950/60 dark:text-slate-100 dark:border-slate-700 font-mono resize-none"}),u.jsxs("div",{className:"flex gap-2 flex-wrap",children:[u.jsx("button",{onClick:x,className:`px-3 py-1.5 text-xs rounded-xl bg-slate-900 text-white hover:bg-slate-800 dark:bg-slate-100 dark:text-slate-900 dark:hover:bg-white ${me}`,children:"↓ Скачать"}),u.jsx("button",{onClick:E,className:`px-3 py-1.5 text-xs rounded-xl border border-slate-300 dark:border-slate-700 hover:bg-slate-50 dark:hover:bg-slate-800 ${me}`,children:g?"✓ Скопировано":"Скопировать"}),u.jsx("button",{onClick:()=>{const w=new URLSearchParams;C.forEach(S=>w.append("category",S.full_name)),w.set("type",m),w.set("format",c),n!=="all"&&w.set("source",n),j(`/export?${w}`)},className:`px-3 py-1.5 text-xs rounded-xl border border-slate-300 dark:border-slate-700 hover:bg-slate-50 dark:hover:bg-slate-800 ${me}`,children:"Открыть экспорт →"})]})]})]})}function Qm({sources:e}){const[t,n]=k.useState([]),[r,l]=k.useState(!0),[s,o]=k.useState("all"),[i,a]=k.useState(new Set(["messengers"]));k.useEffect(()=>{fetch("/api/presets").then(h=>h.json()).then(n).finally(()=>l(!1))},[]);const c=h=>{a(m=>{const v=new Set(m);return v.has(h)?v.delete(h):v.add(h),v})};return r?u.jsx("div",{className:"py-12 text-center text-sm text-slate-400",children:"Загрузка…"}):u.jsxs("div",{className:"space-y-2",children:[e.length>0&&u.jsx("div",{className:`${de} p-3 mb-4`,children:u.jsxs("div",{className:"flex items-center gap-2 flex-wrap",children:[u.jsx("span",{className:"text-xs text-slate-500",children:"Источник:"}),u.jsx("button",{onClick:()=>o("all"),className:`text-xs px-2.5 py-1 rounded-full border transition-colors ${s==="all"?"bg-slate-900 text-white dark:bg-slate-100 dark:text-slate-900 border-transparent":"border-slate-300 dark:border-slate-600 hover:bg-slate-100 dark:hover:bg-slate-800"}`,type:"button",children:"Все"}),e.map(h=>u.jsx("button",{onClick:()=>o(h.slug),className:`text-xs px-2.5 py-1 rounded-full border transition-colors ${s===h.slug?"bg-slate-900 text-white dark:bg-slate-100 dark:text-slate-900 border-transparent":"border-slate-300 dark:border-slate-600 hover:bg-slate-100 dark:hover:bg-slate-800"}`,type:"button",children:h.name},h.slug))]})}),t.map(h=>u.jsxs("div",{className:de,children:[u.jsxs("button",{onClick:()=>c(h.slug),className:`w-full flex items-center justify-between px-4 py-3 ${me}`,type:"button",children:[u.jsxs("div",{className:"flex items-center gap-2",children:[u.jsx("span",{className:"text-sm font-semibold text-slate-900 dark:text-slate-100",children:h.group}),u.jsxs("span",{className:"text-xs text-slate-400",children:[h.services.length," сервисов"]})]}),u.jsx("svg",{className:`w-4 h-4 text-slate-400 transition-transform ${i.has(h.slug)?"rotate-180":""}`,viewBox:"0 0 20 20",fill:"currentColor",children:u.jsx("path",{fillRule:"evenodd",d:"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z",clipRule:"evenodd"})})]}),i.has(h.slug)&&u.jsx("div",{className:"border-t border-slate-100 dark:border-slate-800 p-3 space-y-1.5",children:h.services.map(m=>u.jsx(bm,{svc:m,sources:e,sourceFilter:s},m.name))})]},h.slug))]})}function Km({sources:e,lastUpdate:t,updateRunning:n}){const[r,l]=hi(),[s,o]=k.useState(()=>r.get("q")||""),[i,a]=k.useState(()=>r.get("source")||"all"),[c,h]=k.useState([]),[m,v]=k.useState(!1),g=kr();k.useEffect(()=>{const f=r.get("q");if(f&&f.length>=2){const d=r.get("source")||"all";v(!0);const p=new URLSearchParams({q:f,cat_type:"all",limit:"100"});d!=="all"&&p.set("source",d),fetch(`/api/search?${p}`).then(x=>x.json()).then(h).finally(()=>v(!1))}},[]),k.useEffect(()=>{if(s.trim().length<2){s.trim().length===0&&h([]);return}const f=setTimeout(()=>y(s,i),350);return()=>clearTimeout(f)},[s]);const y=async(f,d)=>{const p=f??s,x=d??i;if(p.trim().length<2)return;v(!0);const E=new URLSearchParams;E.set("q",p),E.set("tab","search"),x!=="all"&&E.set("source",x),l(E,{replace:!0});try{const w=new URLSearchParams({q:p,cat_type:"all",limit:"100"});x!=="all"&&w.set("source",x);const S=await fetch(`/api/search?${w}`);h(await S.json())}finally{v(!1)}},j=f=>{var T;const d=f.full_name,p=f.source_slug,[x,E]=d.split(":",2),w=E.replace(/^geoip_|^geosite_/,""),S=x==="geoip"?"geosite":"geoip",P=((T=c.find($=>$.source_slug===p&&(($==null?void 0:$.full_name)===`${S}:${S}_${w}`||($==null?void 0:$.full_name)===`${S}:${w}`))||c.find($=>($==null?void 0:$.full_name)===`${S}:${S}_${w}`||($==null?void 0:$.full_name)===`${S}:${w}`))==null?void 0:T.full_name)??null,z=new URLSearchParams;z.append("category",d),P&&z.append("category",P),p&&z.set("source",p),g(`/export?${z}`)},C=c.reduce((f,d)=>{const p=d.source_slug;return f[p]||(f[p]=[]),f[p].push(d),f},{});return u.jsxs("div",{children:[u.jsxs("div",{className:`${de} p-4 mb-4`,children:[u.jsx("div",{className:"text-sm mb-3 text-slate-600 dark:text-slate-400",children:"Поиск по категориям (geosite / geoip) из всех источников"}),u.jsxs("div",{className:"relative",children:[u.jsx("input",{value:s,onChange:f=>o(f.target.value),className:`w-full ${wr} pr-10`,placeholder:"Категория (google, ru), домен (whatsapp.com) или слово (whatsapp)…",autoFocus:!0}),m&&u.jsx("div",{className:"absolute right-3 top-1/2 -translate-y-1/2",children:u.jsx("svg",{className:"animate-spin w-4 h-4 text-slate-400",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:u.jsx("path",{d:"M21 12a9 9 0 1 1-6.219-8.56"})})}),!m&&s&&u.jsx("button",{onClick:()=>{o(""),h([]),l({tab:"search"},{replace:!0})},className:"absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 hover:text-slate-600 dark:hover:text-slate-200",type:"button",children:"✕"})]}),e.length>0&&u.jsxs("div",{className:"flex items-center gap-2 mt-3 flex-wrap",children:[u.jsx("span",{className:"text-xs text-slate-500",children:"Источник:"}),u.jsx("button",{onClick:()=>{a("all"),c.length>0&&y(s,"all")},className:`text-xs px-2.5 py-1 rounded-full border transition-colors ${i==="all"?"bg-slate-900 text-white dark:bg-slate-100 dark:text-slate-900 border-transparent":"border-slate-300 dark:border-slate-600 hover:bg-slate-100 dark:hover:bg-slate-800"}`,type:"button",children:"Все"}),e.map(f=>u.jsx("button",{onClick:()=>{a(f.slug),c.length>0&&y(s,f.slug)},className:`text-xs px-2.5 py-1 rounded-full border transition-colors ${i===f.slug?"bg-slate-900 text-white dark:bg-slate-100 dark:text-slate-900 border-transparent":"border-slate-300 dark:border-slate-600 hover:bg-slate-100 dark:hover:bg-slate-800"}`,type:"button",children:f.name},f.slug))]}),u.jsx("div",{className:"flex items-center gap-2 mt-3 pt-3 border-t border-slate-100 dark:border-slate-800",children:n?u.jsxs("div",{className:"flex items-center gap-2 text-xs text-amber-600 dark:text-amber-400",children:[u.jsx("svg",{className:"animate-spin w-3 h-3",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:u.jsx("path",{d:"M21 12a9 9 0 1 1-6.219-8.56"})}),"База обновляется, подождите…"]}):u.jsxs("div",{className:"text-xs text-slate-500",children:["База обновлена: ",u.jsx("span",{className:"text-slate-700 dark:text-slate-300 font-medium",children:t??"нет данных"})]})})]}),Object.keys(C).length>0&&u.jsx("div",{className:"space-y-4",children:Object.entries(C).map(([f,d])=>{const p=e.find(x=>x.slug===f);return u.jsxs("div",{children:[u.jsxs("div",{className:"flex items-center gap-2 mb-2 px-1",children:[u.jsx(Wl,{slug:f,name:(p==null?void 0:p.name)??f}),u.jsx("span",{className:"text-xs text-slate-400",children:p==null?void 0:p.compatible})]}),u.jsx("div",{className:"space-y-1.5",children:d.map(x=>{var E;return u.jsxs("button",{onClick:()=>j(x),className:`w-full text-left ${de} hover:bg-white dark:hover:bg-slate-900/70 px-4 py-3 ${me}`,type:"button",children:[u.jsxs("div",{className:"flex items-center justify-between gap-2",children:[u.jsxs("div",{className:"flex items-center gap-2 min-w-0",children:[u.jsx("span",{className:"text-sm font-medium font-mono truncate",children:kn(x.full_name)}),u.jsx(_l,{text:kn(x.full_name)}),x.match_type==="content"&&u.jsx("span",{className:"text-xs px-1.5 py-0.5 rounded bg-orange-100 text-orange-600 dark:bg-orange-900/40 dark:text-orange-400 shrink-0",children:"по доменам"}),x.match_type==="domain"&&u.jsx("span",{className:"text-xs px-1.5 py-0.5 rounded bg-teal-100 text-teal-600 dark:bg-teal-900/40 dark:text-teal-400 shrink-0",children:"домен найден"})]}),u.jsx(Wm,{type:x.type})]}),x.region&&u.jsxs("div",{className:"text-xs text-slate-500 mt-1",children:["Регион: ",x.region]}),((E=x.matched_values)==null?void 0:E.length)>0&&u.jsx("div",{className:"flex flex-wrap gap-1 mt-2",children:x.matched_values.map(w=>u.jsx("span",{className:"text-xs font-mono px-2 py-0.5 rounded bg-slate-100 dark:bg-slate-800 text-slate-600 dark:text-slate-400",children:w},w))})]},x.full_name+x.source_slug)})})]},f)})}),c.length===0&&s&&!m&&u.jsx("div",{className:"mt-8 text-center text-sm text-slate-400",children:"Ничего не найдено"})]})}const Gm={runetfreedom:"bg-rose-100 text-rose-700 dark:bg-rose-900/40 dark:text-rose-300",loyalsoldier:"bg-violet-100 text-violet-700 dark:bg-violet-900/40 dark:text-violet-300",daniellavrushin:"bg-amber-100 text-amber-700 dark:bg-amber-900/40 dark:text-amber-300",v2fly:"bg-sky-100 text-sky-700 dark:bg-sky-900/40 dark:text-sky-300"};function xs({matches:e}){if(!e||e.length===0)return u.jsx("span",{className:"text-xs text-slate-400",children:"—"});const t=e.filter((n,r,l)=>l.findIndex(s=>s.full_name===n.full_name&&s.source_slug===n.source_slug)===r);return u.jsxs("div",{className:"flex flex-wrap gap-1",children:[t.slice(0,3).map(n=>u.jsx("span",{className:`text-xs px-1.5 py-0.5 rounded-full font-mono ${Gm[n.source_slug]??"bg-slate-100 text-slate-600 dark:bg-slate-800 dark:text-slate-400"}`,children:n.full_name.replace(/^geoip_|^geosite_/,"").split(":").map(r=>r.replace(/^geoip_|^geosite_/,"")).join(":")},n.full_name+n.source_slug)),t.length>3&&u.jsxs("span",{className:"text-xs text-slate-400",children:["+",t.length-3]})]})}function On({item:e,checked:t,onChange:n}){return u.jsxs("label",{className:`flex items-start gap-3 px-3 py-3 sm:py-2.5 rounded-xl border cursor-pointer transition-colors ${t?"bg-slate-50 border-slate-300 dark:bg-slate-800/60 dark:border-slate-600":"border-slate-200 dark:border-slate-800 hover:bg-slate-50 dark:hover:bg-slate-800/30"}`,children:[u.jsx("input",{type:"checkbox",checked:t,onChange:r=>n(e.id,r.target.checked),className:"mt-0.5 shrink-0 accent-slate-900 dark:accent-slate-100 w-4 h-4"}),u.jsxs("div",{className:"min-w-0",children:[u.jsx("div",{className:"text-sm font-medium text-slate-900 dark:text-slate-100",children:e.label}),u.jsx("div",{className:"text-xs text-slate-500 dark:text-slate-400 mt-0.5",children:e.description}),u.jsx("code",{className:"text-xs font-mono text-slate-400 dark:text-slate-500",children:e.value})]})]})}function ys({title:e,description:t,children:n}){return u.jsxs("div",{className:`${de} overflow-hidden`,children:[u.jsxs("div",{className:"px-4 py-3 border-b border-slate-100 dark:border-slate-800",children:[u.jsx("div",{className:"font-semibold text-slate-900 dark:text-slate-100",children:e}),u.jsx("div",{className:"text-xs text-slate-500 dark:text-slate-400 mt-0.5",children:t})]}),u.jsx("div",{className:"p-3 space-y-1.5",children:n})]})}function In({onAdd:e}){const[t,n]=k.useState(""),[r,l]=k.useState([]),[s,o]=k.useState(!1),[i,a]=k.useState(!1),c=Ne.useRef(null),h=Ne.useRef(null),m=g=>{if(g.trim().length<2){l([]),o(!1);return}clearTimeout(c.current),c.current=setTimeout(async()=>{a(!0);try{const y=new URLSearchParams({q:g.trim(),cat_type:"all",limit:"20"}),f=(await(await fetch(`/api/search?${y}`)).json()).map(d=>d.full_name);l(f),o(f.length>0)}finally{a(!1)}},350)},v=g=>{g.trim()&&(e(g.trim()),n(""),l([]),o(!1))};return k.useEffect(()=>{const g=y=>{h.current&&!h.current.contains(y.target)&&o(!1)};return document.addEventListener("mousedown",g),()=>document.removeEventListener("mousedown",g)},[]),u.jsxs("div",{ref:h,className:"relative mt-1.5",children:[u.jsxs("div",{className:"flex gap-2",children:[u.jsxs("div",{className:"relative flex-1",children:[u.jsx("input",{value:t,onChange:g=>{n(g.target.value),m(g.target.value)},onKeyDown:g=>{g.key==="Enter"&&v(t),g.key==="Escape"&&o(!1)},onFocus:g=>{r.length>0&&o(!0),setTimeout(()=>g.target.scrollIntoView({behavior:"smooth",block:"center"}),100)},placeholder:"Своя категория (напр. geosite:google)",className:`${wr} w-full text-xs pr-7`}),i&&u.jsx("span",{className:"absolute right-2.5 top-1/2 -translate-y-1/2 text-slate-400 text-xs",children:"…"})]}),u.jsx("button",{onClick:()=>v(t),className:"px-3 py-1.5 text-xs rounded-xl border border-slate-300 dark:border-slate-700 hover:bg-slate-50 dark:hover:bg-slate-800 text-slate-600 dark:text-slate-300 shrink-0",children:"+ Добавить"})]}),s&&r.length>0&&u.jsx("div",{className:"absolute z-50 left-0 right-12 mt-1 rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 shadow-lg overflow-hidden max-h-52 overflow-y-auto",children:r.map(g=>u.jsx("button",{onMouseDown:y=>{y.preventDefault(),v(g)},className:"w-full text-left px-3 py-2 text-xs font-mono hover:bg-slate-50 dark:hover:bg-slate-800 text-slate-700 dark:text-slate-300 border-b border-slate-100 dark:border-slate-800 last:border-0",children:g},g))})]})}function Xm(){const[e,t]=k.useState(null),[n,r]=k.useState(!0),[l,s]=k.useState(!1),[o,i]=k.useState(null),[a,c]=k.useState(!1),[h,m]=k.useState("GeoExport"),[v,g]=k.useState("loyalsoldier"),[y,j]=k.useState("block-direct-proxy"),[C,f]=k.useState(!1),[d,p]=k.useState({}),[x,E]=k.useState({direct_sites:[],direct_ips:[],proxy_sites:[],proxy_ips:[],block_sites:[],block_ips:[]}),[w,S]=k.useState({direct_sites:{},direct_ips:{},proxy_sites:{},proxy_ips:{},block_sites:{},block_ips:{}});k.useEffect(()=>{fetch("/api/routing/presets").then(N=>N.json()).then(N=>{t(N);const U={};for(const F of Object.values(N.presets))for(const V of F)U[V.id]=V.checked;p(U)}).finally(()=>r(!1))},[]);const P=(N,U)=>p(F=>({...F,[N]:U})),z=(N,U)=>{E(F=>({...F,[N]:[...F[N]||[],U]})),S(F=>({...F,[N]:{...F[N]||{},[U]:!0}}))},T=(N,U,F)=>{S(V=>({...V,[N]:{...V[N]||{},[U]:F}}))},$=N=>{if(!e)return[];const U=e.presets[N].filter(V=>d[V.id]).map(V=>V.value),F=(x[N]||[]).filter(V=>{var L;return(L=w[N])==null?void 0:L[V]});return[...U,...F]},Et=async()=>{s(!0),i(null);try{const U=await(await fetch("/api/routing/generate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:h,geo_source:v,global_proxy:C,route_order:y,direct_sites:$("direct_sites"),direct_ips:$("direct_ips"),proxy_sites:$("proxy_sites"),proxy_ips:$("proxy_ips"),block_sites:$("block_sites"),block_ips:$("block_ips")})})).json();i(U)}finally{s(!1)}},Ct=N=>{navigator.clipboard.writeText(N),c(!0),setTimeout(()=>c(!1),1500)};return n?u.jsx("div",{className:"py-12 text-center text-sm text-slate-400",children:"Загрузка…"}):e?u.jsxs("div",{className:"space-y-4",children:[u.jsxs("div",{className:`${de} p-4`,children:[u.jsx("h2",{className:"text-base font-semibold mb-0.5",children:"Маршрутизация для приложений"}),u.jsxs("p",{className:"text-xs text-slate-500 dark:text-slate-400",children:["Настройте правила и получите deeplink для ",u.jsx("strong",{children:"Happ"})," (iOS / Android / macOS / Windows). Приложение автоматически применит выбранные правила."]})]}),u.jsxs("div",{className:`${de} p-4`,children:[u.jsx("div",{className:"text-xs font-semibold text-slate-500 uppercase tracking-wide mb-3",children:"Настройки профиля"}),u.jsxs("div",{className:"grid grid-cols-1 gap-3",children:[u.jsxs("div",{children:[u.jsx("label",{className:"text-xs text-slate-500 mb-1 block",children:"Название профиля"}),u.jsx("input",{value:h,onChange:N=>m(N.target.value),className:wr,placeholder:"GeoExport"})]}),u.jsxs("div",{children:[u.jsx("label",{className:"text-xs text-slate-500 mb-1 block",children:"База geoip/geosite"}),u.jsx("select",{value:v,onChange:N=>g(N.target.value),className:Vt,children:Object.entries(e.geo_sources).map(([N,U])=>u.jsx("option",{value:N,children:U.label},N))})]}),u.jsxs("div",{children:[u.jsx("label",{className:"text-xs text-slate-500 mb-1 block",children:"Порядок правил"}),u.jsx("select",{value:y,onChange:N=>j(N.target.value),className:Vt,children:e.route_orders.map(N=>u.jsx("option",{value:N.value,children:N.label},N.value))})]}),u.jsxs("div",{className:"flex items-center gap-3 pt-5",children:[u.jsxs("label",{className:"flex items-center gap-2 cursor-pointer",children:[u.jsx("input",{type:"checkbox",checked:C,onChange:N=>f(N.target.checked),className:"accent-slate-900 dark:accent-slate-100"}),u.jsx("span",{className:"text-sm text-slate-700 dark:text-slate-300",children:"Глобальный прокси"})]}),u.jsx("span",{className:"text-xs text-slate-400",children:"Весь трафик через VPN"})]})]})]}),u.jsxs(ys,{title:"Блокировать",description:"Домены и IP блокируются полностью — ни через VPN, ни напрямую. Для блокировки рекламы и трекеров.",children:[e.presets.block_sites.map(N=>u.jsx(On,{item:N,checked:!!d[N.id],onChange:P},N.id)),(x.block_sites||[]).map(N=>{var U,F;return u.jsxs("label",{className:`flex items-start gap-3 px-3 py-2.5 rounded-xl border cursor-pointer transition-colors ${(U=w.block_sites)!=null&&U[N]?"bg-slate-50 border-slate-300 dark:bg-slate-800/60 dark:border-slate-600":"border-slate-200 dark:border-slate-800 hover:bg-slate-50 dark:hover:bg-slate-800/30"}`,children:[u.jsx("input",{type:"checkbox",checked:!!((F=w.block_sites)!=null&&F[N]),onChange:V=>T("block_sites",N,V.target.checked),className:"mt-0.5 shrink-0 accent-slate-900 dark:accent-slate-100 w-4 h-4"}),u.jsxs("div",{className:"min-w-0",children:[u.jsx("div",{className:"text-sm font-medium text-slate-900 dark:text-slate-100",children:"Своя категория"}),u.jsx("code",{className:"text-xs font-mono text-slate-400 dark:text-slate-500",children:N})]})]},N)}),u.jsx(In,{onAdd:N=>z("block_sites",N)})]}),u.jsxs(ys,{title:"Прямой трафик (без VPN)",description:"Сайты и IP открываются напрямую, минуя VPN. Добавьте российские сервисы — это ускорит соединение и снизит нагрузку на сервер.",children:[u.jsx("div",{className:"text-xs font-medium text-slate-500 mb-1",children:"Домены"}),e.presets.direct_sites.map(N=>u.jsx(On,{item:N,checked:!!d[N.id],onChange:P},N.id)),(x.direct_sites||[]).map(N=>{var U,F;return u.jsxs("label",{className:`flex items-start gap-3 px-3 py-2.5 rounded-xl border cursor-pointer transition-colors ${(U=w.direct_sites)!=null&&U[N]?"bg-slate-50 border-slate-300 dark:bg-slate-800/60 dark:border-slate-600":"border-slate-200 dark:border-slate-800 hover:bg-slate-50 dark:hover:bg-slate-800/30"}`,children:[u.jsx("input",{type:"checkbox",checked:!!((F=w.direct_sites)!=null&&F[N]),onChange:V=>T("direct_sites",N,V.target.checked),className:"mt-0.5 shrink-0 accent-slate-900 dark:accent-slate-100 w-4 h-4"}),u.jsxs("div",{className:"min-w-0",children:[u.jsx("div",{className:"text-sm font-medium text-slate-900 dark:text-slate-100",children:"Своя категория"}),u.jsx("code",{className:"text-xs font-mono text-slate-400 dark:text-slate-500",children:N})]})]},N)}),u.jsx(In,{onAdd:N=>z("direct_sites",N)}),u.jsx("div",{className:"text-xs font-medium text-slate-500 mt-3 mb-1",children:"IP-адреса"}),e.presets.direct_ips.map(N=>u.jsx(On,{item:N,checked:!!d[N.id],onChange:P},N.id)),(x.direct_ips||[]).map(N=>{var U,F;return u.jsxs("label",{className:`flex items-start gap-3 px-3 py-2.5 rounded-xl border cursor-pointer transition-colors ${(U=w.direct_ips)!=null&&U[N]?"bg-slate-50 border-slate-300 dark:bg-slate-800/60 dark:border-slate-600":"border-slate-200 dark:border-slate-800 hover:bg-slate-50 dark:hover:bg-slate-800/30"}`,children:[u.jsx("input",{type:"checkbox",checked:!!((F=w.direct_ips)!=null&&F[N]),onChange:V=>T("direct_ips",N,V.target.checked),className:"mt-0.5 shrink-0 accent-slate-900 dark:accent-slate-100 w-4 h-4"}),u.jsxs("div",{className:"min-w-0",children:[u.jsx("div",{className:"text-sm font-medium text-slate-900 dark:text-slate-100",children:"Своя категория"}),u.jsx("code",{className:"text-xs font-mono text-slate-400 dark:text-slate-500",children:N})]})]},N)}),u.jsx(In,{onAdd:N=>z("direct_ips",N)})]}),u.jsxs(ys,{title:"Через VPN",description:"Сайты и IP направляются через VPN-сервер. Добавьте заблокированные в РФ сервисы.",children:[u.jsx("div",{className:"text-xs font-medium text-slate-500 mb-1",children:"Домены"}),e.presets.proxy_sites.map(N=>u.jsx(On,{item:N,checked:!!d[N.id],onChange:P},N.id)),(x.proxy_sites||[]).map(N=>{var U,F;return u.jsxs("label",{className:`flex items-start gap-3 px-3 py-2.5 rounded-xl border cursor-pointer transition-colors ${(U=w.proxy_sites)!=null&&U[N]?"bg-slate-50 border-slate-300 dark:bg-slate-800/60 dark:border-slate-600":"border-slate-200 dark:border-slate-800 hover:bg-slate-50 dark:hover:bg-slate-800/30"}`,children:[u.jsx("input",{type:"checkbox",checked:!!((F=w.proxy_sites)!=null&&F[N]),onChange:V=>T("proxy_sites",N,V.target.checked),className:"mt-0.5 shrink-0 accent-slate-900 dark:accent-slate-100 w-4 h-4"}),u.jsxs("div",{className:"min-w-0",children:[u.jsx("div",{className:"text-sm font-medium text-slate-900 dark:text-slate-100",children:"Своя категория"}),u.jsx("code",{className:"text-xs font-mono text-slate-400 dark:text-slate-500",children:N})]})]},N)}),u.jsx(In,{onAdd:N=>z("proxy_sites",N)}),u.jsx("div",{className:"text-xs font-medium text-slate-500 mt-3 mb-1",children:"IP-адреса"}),e.presets.proxy_ips.map(N=>u.jsx(On,{item:N,checked:!!d[N.id],onChange:P},N.id)),(x.proxy_ips||[]).map(N=>{var U,F;return u.jsxs("label",{className:`flex items-start gap-3 px-3 py-2.5 rounded-xl border cursor-pointer transition-colors ${(U=w.proxy_ips)!=null&&U[N]?"bg-slate-50 border-slate-300 dark:bg-slate-800/60 dark:border-slate-600":"border-slate-200 dark:border-slate-800 hover:bg-slate-50 dark:hover:bg-slate-800/30"}`,children:[u.jsx("input",{type:"checkbox",checked:!!((F=w.proxy_ips)!=null&&F[N]),onChange:V=>T("proxy_ips",N,V.target.checked),className:"mt-0.5 shrink-0 accent-slate-900 dark:accent-slate-100 w-4 h-4"}),u.jsxs("div",{className:"min-w-0",children:[u.jsx("div",{className:"text-sm font-medium text-slate-900 dark:text-slate-100",children:"Своя категория"}),u.jsx("code",{className:"text-xs font-mono text-slate-400 dark:text-slate-500",children:N})]})]},N)}),u.jsx(In,{onAdd:N=>z("proxy_ips",N)})]}),u.jsx("div",{className:"flex justify-center px-0",children:u.jsx("button",{onClick:Et,disabled:l,className:`w-full sm:w-auto px-8 py-3 text-sm font-medium rounded-xl bg-slate-900 text-white hover:bg-slate-800 dark:bg-slate-100 dark:text-slate-900 dark:hover:bg-white disabled:opacity-50 ${me}`,children:l?"Генерация…":"Сгенерировать и получить ссылку"})}),o&&u.jsxs("div",{className:`${de} p-4 pb-5`,style:{borderColor:"rgb(167 243 208)",borderWidth:"2px"},children:[u.jsx("div",{className:"text-sm font-semibold text-emerald-700 dark:text-emerald-300 mb-3",children:"Профиль сгенерирован"}),Object.entries(o.instructions).map(([N,U])=>u.jsxs("div",{className:"space-y-2",children:[u.jsxs("div",{className:"text-xs text-slate-500",children:[U.name," (",U.platforms.join(", "),")"]}),u.jsxs("div",{className:"text-xs text-slate-500",children:[u.jsx("span",{className:"font-medium",children:"Инструкция:"})," ",U.steps.join(" → ")]}),u.jsx("div",{className:"mt-3 pt-2 border-t border-slate-100 dark:border-slate-800",children:u.jsxs("div",{className:"text-xs font-mono text-slate-400 break-all mb-3",children:[o.deeplink.slice(0,100),"…"]})}),u.jsxs("div",{className:"flex flex-col sm:flex-row items-stretch sm:items-center gap-2",children:[u.jsxs("a",{href:o.deeplink,className:`w-full sm:w-auto text-center px-4 py-3 sm:py-2 text-sm rounded-xl bg-slate-900 text-white hover:bg-slate-800 dark:bg-slate-100 dark:text-slate-900 dark:hover:bg-white ${me}`,children:["Открыть в ",U.name]}),u.jsx("button",{onClick:()=>Ct(o.deeplink),className:`w-full sm:w-auto text-center px-3 py-3 sm:py-2 text-xs rounded-xl border border-slate-300 dark:border-slate-700 hover:bg-slate-50 dark:hover:bg-slate-800 ${me}`,children:a?"✓ Скопировано":"Скопировать ссылку"})]})]},N))]})]}):null}function Ym(){const[e,t]=k.useState(""),[n,r]=k.useState(!1),[l,s]=k.useState(null),[o,i]=k.useState(null),[a,c]=k.useState("plain-lines");kr();const h=async()=>{const g=e.trim();if(g){r(!0),i(null),s(null);try{const y=await fetch(`/api/lookup?domain=${encodeURIComponent(g)}`);if(!y.ok){const j=await y.json().catch(()=>({}));i(j.detail??"Ошибка запроса");return}s(await y.json())}catch(y){i(y.message??"Ошибка сети")}finally{r(!1)}}},m=()=>{if(!l)return;const g=l.all_ips_v4;let y="";a==="plain-lines"?y=g.join(` +`):a==="keenetic-ip"?y=g.map(d=>{const p=d.split("/"),x=p[0],E=parseInt(p[1]??"32"),w=E===32?"255.255.255.255":Array.from({length:4},(S,P)=>65280>>Math.min(8,Math.max(0,E-P*8))&255).join(".");return`route ADD ${x} MASK ${w} 0.0.0.0 & rem ${l.domain}`}).join(` +`):y=g.join(",");const j=new Blob([y],{type:"text/plain;charset=utf-8"}),C=URL.createObjectURL(j),f=document.createElement("a");f.href=C,f.download=`${l.domain}_ips.txt`,f.click(),URL.revokeObjectURL(C)},v=g=>!g||g.length!==2?"":String.fromCodePoint(...[...g.toUpperCase()].map(y=>127391+y.charCodeAt(0)));return u.jsxs("div",{children:[u.jsxs("div",{className:`${de} p-4 mb-4`,children:[u.jsx("div",{className:"text-sm mb-3 text-slate-600 dark:text-slate-400",children:"Введите домен для получения IP адресов и субдоменов. Результаты проверяются в базах geoip/geosite"}),u.jsxs("div",{className:"flex gap-2",children:[u.jsx("input",{value:e,onChange:g=>t(g.target.value),onKeyDown:g=>g.key==="Enter"&&h(),className:`flex-1 ${wr}`,placeholder:"например: example.com, anydesk.com…",autoFocus:!0}),u.jsx("button",{onClick:h,disabled:n,className:`px-4 py-2 text-sm rounded-xl bg-slate-900 text-white hover:bg-slate-800 dark:bg-slate-100 dark:text-slate-900 dark:hover:bg-white disabled:opacity-50 ${me}`,type:"button",children:n?u.jsxs("span",{className:"flex items-center gap-2",children:[u.jsx("svg",{className:"animate-spin w-4 h-4",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:u.jsx("path",{d:"M21 12a9 9 0 1 1-6.219-8.56"})}),"Поиск…"]}):"Найти"})]}),o&&u.jsxs("div",{className:"mt-2 text-xs text-red-600 dark:text-red-400 bg-red-50 dark:bg-red-900/20 rounded-lg px-3 py-2",children:["Ошибка: ",o]})]}),l&&u.jsxs("div",{className:"space-y-3",children:[u.jsxs("div",{className:`${de} p-4`,children:[u.jsxs("div",{className:"flex items-center justify-between flex-wrap gap-2 mb-3",children:[u.jsxs("div",{children:[u.jsx("span",{className:"text-base font-semibold font-mono",children:l.domain}),u.jsxs("span",{className:"ml-2 text-xs text-slate-500",children:[l.subdomains_total," субдоменов · ",l.all_ips.length," уникальных IP"]})]}),u.jsxs("div",{className:"flex items-center gap-2",children:[u.jsxs("select",{value:a,onChange:g=>c(g.target.value),className:`text-xs ${Vt}`,children:[u.jsx("option",{value:"plain-lines",children:"Построчно"}),u.jsx("option",{value:"single-line",children:"Одной строкой"}),u.jsx("option",{value:"keenetic-ip",children:"Keenetic .bat"})]}),u.jsxs("button",{onClick:m,className:`px-3 py-1.5 text-xs rounded-xl bg-slate-900 text-white hover:bg-slate-800 dark:bg-slate-100 dark:text-slate-900 ${me}`,children:["↓ Скачать IPv4 (",l.all_ips_v4.length,")"]})]})]}),u.jsxs("div",{className:"mb-3 pb-3 border-b border-slate-100 dark:border-slate-800",children:[u.jsx("div",{className:"text-xs font-medium text-slate-500 mb-2",children:"Основной домен"}),u.jsx("div",{className:"flex items-start gap-3 flex-wrap",children:u.jsxs("div",{children:[u.jsxs("div",{className:"flex flex-wrap gap-1 mb-1",children:[l.main_dns.ipv4.map(g=>u.jsx("span",{className:"text-xs font-mono px-2 py-0.5 rounded bg-blue-50 dark:bg-blue-900/20 text-blue-700 dark:text-blue-300",children:g},g)),l.main_dns.ipv6.map(g=>u.jsx("span",{className:"text-xs font-mono px-2 py-0.5 rounded bg-slate-100 dark:bg-slate-800 text-slate-600 dark:text-slate-400",children:g},g)),l.main_dns.cname&&u.jsxs("span",{className:"text-xs font-mono px-2 py-0.5 rounded bg-amber-50 dark:bg-amber-900/20 text-amber-700 dark:text-amber-300",children:["CNAME: ",l.main_dns.cname]})]}),u.jsx(xs,{matches:l.main_dns.in_db})]})})]}),l.all_ips.length>0&&u.jsxs("div",{children:[u.jsx("div",{className:"text-xs font-medium text-slate-500 mb-2",children:"Все IP адреса"}),u.jsx("div",{className:"space-y-1 max-h-64 overflow-y-auto",children:l.all_ips.map(g=>u.jsxs("div",{className:"flex items-center gap-2 text-xs py-1 border-b border-slate-50 dark:border-slate-900 last:border-0",children:[u.jsx("span",{className:"font-mono text-slate-700 dark:text-slate-300 w-36 shrink-0",children:g.ip}),g.country_code&&u.jsx("span",{className:"shrink-0",children:v(g.country_code)}),u.jsx("span",{className:"text-slate-500 truncate flex-1",children:g.org||g.asn}),u.jsx(xs,{matches:g.in_db})]},g.ip))})]})]}),l.subdomains.length>0&&u.jsxs("div",{className:`${de} p-4`,children:[u.jsxs("div",{className:"text-xs font-medium text-slate-500 mb-3",children:["Субдомены (",l.subdomains.length," из ",l.subdomains_total,")"]}),u.jsx("div",{className:"space-y-1.5 max-h-96 overflow-y-auto",children:l.subdomains.map(g=>u.jsxs("div",{className:"flex items-start gap-2 text-xs py-1.5 border-b border-slate-50 dark:border-slate-900 last:border-0",children:[u.jsx("span",{className:"font-mono text-slate-700 dark:text-slate-300 w-56 shrink-0 truncate",children:g.domain}),u.jsxs("div",{className:"flex flex-wrap gap-1 flex-1 min-w-0",children:[g.ipv4.slice(0,3).map(y=>u.jsx("span",{className:"font-mono px-1.5 py-0.5 rounded bg-blue-50 dark:bg-blue-900/20 text-blue-700 dark:text-blue-300",children:y},y)),g.ipv4.length>3&&u.jsxs("span",{className:"text-slate-400",children:["+",g.ipv4.length-3]}),g.cname&&u.jsxs("span",{className:"font-mono px-1.5 py-0.5 rounded bg-amber-50 dark:bg-amber-900/20 text-amber-700 dark:text-amber-300 truncate max-w-xs",children:["→ ",g.cname]})]}),u.jsx(xs,{matches:g.in_db})]},g.domain))})]})]})]})}function Jm(){const[e,t]=hi(),[n,r]=k.useState(()=>e.get("tab")||"popular"),[l,s]=k.useState([]),[o,i]=k.useState(null),[a,c]=k.useState(!1);k.useEffect(()=>{fetch("/api/last-update").then(m=>m.json()).then(m=>i(m.last_update)),fetch("/api/update/status").then(m=>m.json()).then(m=>c(m.running)).catch(()=>{}),fetch("/api/sources").then(m=>m.json()).then(s).catch(()=>{})},[]),k.useEffect(()=>{if(!a)return;const m=setInterval(async()=>{const v=await fetch("/api/update/status").then(g=>g.json()).catch(()=>({running:!1}));if(c(v.running),!v.running){const g=await fetch("/api/last-update").then(y=>y.json());i(g.last_update)}},3e3);return()=>clearInterval(m)},[a]);const h=m=>{r(m),t({tab:m},{replace:!0})};return u.jsx("div",{className:"min-h-screen bg-slate-100 text-slate-900 dark:bg-slate-950 dark:text-slate-100 px-3 py-4 sm:px-4 sm:py-8",children:u.jsxs("div",{className:"max-w-3xl mx-auto w-full",children:[u.jsx(vi,{tab:n,setTab:h}),n==="popular"&&u.jsx(Qm,{sources:l}),n==="search"&&u.jsx(Km,{sources:l,lastUpdate:o,updateRunning:a}),n==="lookup"&&u.jsx(Ym,{}),n==="routing"&&u.jsx(Xm,{})]})})}function Zm(){const[e,t]=k.useState([]),[n,r]=k.useState(null);return k.useEffect(()=>{fetch("/api/sources").then(l=>l.json()).then(t).catch(()=>{}),fetch("/api/last-update").then(l=>l.json()).then(r).catch(()=>{})},[]),u.jsx("div",{className:"min-h-screen bg-slate-100 text-slate-900 dark:bg-slate-950 dark:text-slate-100 px-3 py-4 sm:px-4 sm:py-8",children:u.jsxs("div",{className:"max-w-3xl mx-auto w-full",children:[u.jsx(vi,{back:{to:"/",label:"Назад"}}),u.jsx("h1",{className:"text-lg font-semibold mb-4",children:"Источники баз данных"}),u.jsx("div",{className:"space-y-3",children:e.map(l=>{var o;const s=(o=n==null?void 0:n.sources)==null?void 0:o.find(i=>i.source_slug===l.slug);return u.jsx("div",{className:`${de} p-4`,children:u.jsx("div",{className:"flex items-start justify-between gap-3 flex-wrap",children:u.jsxs("div",{className:"flex-1 min-w-0",children:[u.jsxs("div",{className:"flex items-center gap-2 mb-1",children:[u.jsx(Wl,{slug:l.slug,name:l.name}),u.jsx("span",{className:"text-xs text-slate-500",children:s!=null&&s.last_update?`обновлено: ${s.last_update}`:"нет данных"})]}),u.jsx("div",{className:"text-sm text-slate-700 dark:text-slate-300 mb-2",children:l.description}),u.jsxs("div",{className:"text-xs text-slate-500",children:["Совместимо с: ",u.jsx("span",{className:"font-medium text-slate-700 dark:text-slate-300",children:l.compatible})]}),u.jsxs("div",{className:"mt-2 space-y-1",children:[l.geoip_url&&u.jsxs("div",{className:"text-xs",children:[u.jsx("span",{className:"text-slate-400 mr-1",children:"geoip.dat:"}),u.jsx("a",{href:l.geoip_url,target:"_blank",rel:"noreferrer",className:"font-mono text-blue-600 dark:text-blue-400 hover:underline break-all",children:l.geoip_url})]}),l.geosite_url&&u.jsxs("div",{className:"text-xs",children:[u.jsx("span",{className:"text-slate-400 mr-1",children:"geosite.dat:"}),u.jsx("a",{href:l.geosite_url,target:"_blank",rel:"noreferrer",className:"font-mono text-blue-600 dark:text-blue-400 hover:underline break-all",children:l.geosite_url})]}),!l.geosite_url&&u.jsx("div",{className:"text-xs text-slate-400 italic",children:"geosite.dat — нет в этом источнике"})]})]})})},l.slug)})}),u.jsxs("div",{className:`${de} p-4 mt-4 text-sm text-slate-600 dark:text-slate-400`,children:[u.jsx("p",{className:"font-medium text-slate-800 dark:text-slate-200 mb-1",children:"Как использовать с 3x-ui / Xray"}),u.jsxs("p",{className:"text-xs",children:["Скопируйте имя категории (например ",u.jsx("code",{className:"font-mono bg-slate-100 dark:bg-slate-800 px-1 rounded",children:"geoip:akamai"}),") и вставьте в поле маршрутизации 3x-ui. Используйте только категории из источников ",u.jsx("strong",{children:"RuNet Freedom"}),", ",u.jsx("strong",{children:"Loyalsoldier"})," или ",u.jsx("strong",{children:"DanielLavrushin"})," — они совместимы с Xray."]})]})]})})}function qm(){const[e]=hi(),t=e.getAll("category"),n=e.get("source"),[r,l]=k.useState("domains,ip4,ip6"),[s,o]=k.useState("plain-lines"),[i,a]=k.useState(""),[c,h]=k.useState(0),[m,v]=k.useState(null),[g,y]=k.useState(!1),[j,C]=k.useState(null),[f,d]=k.useState(null);k.useEffect(()=>{fetch("/api/last-update").then(S=>S.json()).then(S=>v(S.last_update)),n&&fetch("/api/sources").then(S=>S.json()).then(S=>{d(S.find(P=>P.slug===n)??null)}).catch(()=>{})},[n]),k.useEffect(()=>{s==="keenetic-ip"&&!r.includes("ip4")&&!r.includes("ip6")&&o("plain-lines")},[r]);const p=k.useCallback(async()=>{if(t.length){y(!0),C(null);try{const S=new URLSearchParams;t.forEach(T=>S.append("category",T)),S.set("type",r),S.set("format",s==="single-line"?"plain-lines":s),n&&S.set("source",n);const P=await fetch(`/api/export?${S}`);if(!P.ok){const T=await P.json().catch(()=>({}));C(T.detail??P.statusText),a(""),h(0);return}let z=await P.text();s==="single-line"&&(z=z.trim().split(/\r?\n/).join(",")),a(z),h(z.trim()?z.trim().split(/[,\r?\n]/).filter(Boolean).length:0)}finally{y(!1)}}},[t.join(","),r,s,n]);k.useEffect(()=>{p()},[p]);const x=()=>navigator.clipboard.writeText(i),E=()=>{const S=new Blob([i],{type:"text/plain;charset=utf-8"}),P=URL.createObjectURL(S),z=document.createElement("a");z.href=P,z.download=`export_${(t[0]??"data").replace(":","_")}.txt`,z.click(),URL.revokeObjectURL(P)},w=`${window.location.origin}/api/export?`+t.map(S=>`category=${encodeURIComponent(S)}`).join("&")+`&type=${encodeURIComponent(r)}&format=${encodeURIComponent(s)}`+(n?`&source=${encodeURIComponent(n)}`:"");return u.jsx("div",{className:"min-h-screen bg-slate-100 text-slate-900 dark:bg-slate-950 dark:text-slate-100 px-3 py-4 sm:px-4 sm:py-8",children:u.jsxs("div",{className:"max-w-3xl mx-auto w-full",children:[u.jsx(vi,{back:{to:"/",label:"Назад"}}),u.jsxs("div",{className:`${de} p-4 mb-4`,children:[u.jsxs("div",{className:"flex items-start justify-between gap-2 flex-wrap mb-4",children:[u.jsxs("div",{className:"flex-1 min-w-0",children:[u.jsx("div",{className:"text-xs text-slate-500 mb-1",children:"Категории:"}),u.jsx("div",{className:"flex flex-wrap gap-1 mb-2",children:t.map(S=>u.jsxs("span",{className:`inline-flex items-center gap-1 text-xs px-2 py-0.5 rounded-full font-mono ${S.startsWith("geoip:")?"bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300":"bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-300"}`,children:[kn(S),u.jsx(_l,{text:kn(S)})]},S))}),f&&u.jsxs("div",{className:"flex items-center gap-2 text-xs text-slate-500",children:[u.jsx("span",{children:"Источник:"}),u.jsx(Wl,{slug:f.slug,name:f.name}),u.jsxs("span",{className:"text-slate-400",children:["· ",f.compatible]})]})]}),u.jsx("div",{className:"text-sm font-medium tabular-nums text-slate-600 dark:text-slate-400",children:g?"…":`${c.toLocaleString()} строк`})]}),u.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-2",children:[u.jsxs("select",{value:r,onChange:S=>l(S.target.value),className:Vt,children:[u.jsx("option",{value:"domains,ip4,ip6",children:"Домены + IPv4 + IPv6"}),u.jsx("option",{value:"domains,ip4",children:"Домены + IPv4"}),u.jsx("option",{value:"ip4,ip6",children:"IPv4 + IPv6"}),u.jsx("option",{value:"domains",children:"Только домены"}),u.jsx("option",{value:"ip4",children:"Только IPv4"}),u.jsx("option",{value:"ip6",children:"Только IPv6"})]}),u.jsx("select",{value:s,onChange:S=>o(S.target.value),className:Vt,children:sd.map(S=>u.jsx("option",{value:S.value,children:S.label},S.value))})]}),s==="keenetic-ip"&&u.jsxs("div",{className:"mt-2 text-xs text-slate-500 bg-slate-50 dark:bg-slate-900 rounded-lg px-3 py-2",children:["Формат: ",u.jsx("code",{className:"font-mono",children:"route ADD MASK <маска> 0.0.0.0 & rem <категория>"})]}),j&&u.jsxs("div",{className:"mt-2 text-xs text-red-600 dark:text-red-400 bg-red-50 dark:bg-red-900/20 rounded-lg px-3 py-2",children:["Ошибка: ",j]}),u.jsxs("div",{className:"flex gap-2 mt-4",children:[u.jsx("button",{onClick:E,className:`px-4 py-2 text-sm rounded-xl bg-slate-900 text-white hover:bg-slate-800 dark:bg-slate-100 dark:text-slate-900 dark:hover:bg-white ${me}`,children:"↓ Скачать"}),u.jsx("button",{onClick:x,className:`px-4 py-2 text-sm rounded-xl border border-slate-300 dark:border-slate-700 hover:bg-slate-50 dark:hover:bg-slate-800 ${me}`,children:"Скопировать"})]}),u.jsxs("div",{className:"mt-4 pt-3 border-t border-slate-100 dark:border-slate-800",children:[u.jsx("div",{className:"text-xs text-slate-500 mb-1",children:"Прямая ссылка на API:"}),u.jsxs("div",{className:"flex items-center gap-2",children:[u.jsx("div",{className:"flex-1 text-xs break-all font-mono bg-slate-50 dark:bg-slate-900 rounded-lg px-3 py-2 text-slate-700 dark:text-slate-300 select-all cursor-text",children:w}),u.jsx(_l,{text:w})]})]}),u.jsxs("div",{className:"text-xs text-slate-500 mt-2",children:["Обновлено: ",u.jsx("span",{className:"text-slate-700 dark:text-slate-300",children:m??"—"})]})]}),u.jsx("div",{className:`${de} p-4`,children:u.jsx("textarea",{value:g?"Загрузка…":j?"":i,readOnly:!0,className:"w-full h-80 rounded-xl px-3 py-2 text-xs border bg-white text-slate-900 border-slate-200 dark:bg-slate-950/60 dark:text-slate-100 dark:border-slate-700 font-mono resize-none"})})]})})}function e0(){return u.jsxs(Cm,{children:[u.jsx(qr,{path:"/",element:u.jsx(Jm,{})}),u.jsx(qr,{path:"/sources",element:u.jsx(Zm,{})}),u.jsx(qr,{path:"/export",element:u.jsx(qm,{})})]})}ks.createRoot(document.getElementById("root")).render(u.jsx(Ne.StrictMode,{children:u.jsx(Um,{children:u.jsx(e0,{})})})); diff --git a/site/assets/index-uS40y9LP.css b/site/assets/index-uS40y9LP.css new file mode 100644 index 0000000..0fa7f57 --- /dev/null +++ b/site/assets/index-uS40y9LP.css @@ -0,0 +1 @@ +*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.absolute{position:absolute}.relative{position:relative}.left-0{left:0}.right-12{right:3rem}.right-2\.5{right:.625rem}.right-3{right:.75rem}.top-1\/2{top:50%}.z-50{z-index:50}.-mx-4{margin-left:-1rem;margin-right:-1rem}.mx-auto{margin-left:auto;margin-right:auto}.mb-0\.5{margin-bottom:.125rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.ml-2{margin-left:.5rem}.mr-1{margin-right:.25rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-8{margin-top:2rem}.block{display:block}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.h-28{height:7rem}.h-3{height:.75rem}.h-4{height:1rem}.h-7{height:1.75rem}.h-80{height:20rem}.max-h-52{max-height:13rem}.max-h-64{max-height:16rem}.max-h-96{max-height:24rem}.min-h-screen{min-height:100vh}.w-3{width:.75rem}.w-36{width:9rem}.w-4{width:1rem}.w-56{width:14rem}.w-7{width:1.75rem}.w-full{width:100%}.min-w-0{min-width:0px}.max-w-3xl{max-width:48rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-180{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.cursor-text{cursor:text}.select-all{-webkit-user-select:all;-moz-user-select:all;user-select:all}.resize-none{resize:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.items-stretch{align-items:stretch}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.375rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-slate-100{--tw-border-opacity: 1;border-color:rgb(241 245 249 / var(--tw-border-opacity, 1))}.border-slate-200{--tw-border-opacity: 1;border-color:rgb(226 232 240 / var(--tw-border-opacity, 1))}.border-slate-300{--tw-border-opacity: 1;border-color:rgb(203 213 225 / var(--tw-border-opacity, 1))}.border-slate-50{--tw-border-opacity: 1;border-color:rgb(248 250 252 / var(--tw-border-opacity, 1))}.border-transparent{border-color:transparent}.bg-amber-100{--tw-bg-opacity: 1;background-color:rgb(254 243 199 / var(--tw-bg-opacity, 1))}.bg-amber-50{--tw-bg-opacity: 1;background-color:rgb(255 251 235 / var(--tw-bg-opacity, 1))}.bg-blue-100{--tw-bg-opacity: 1;background-color:rgb(219 234 254 / var(--tw-bg-opacity, 1))}.bg-blue-50{--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1))}.bg-emerald-100{--tw-bg-opacity: 1;background-color:rgb(209 250 229 / var(--tw-bg-opacity, 1))}.bg-orange-100{--tw-bg-opacity: 1;background-color:rgb(255 237 213 / var(--tw-bg-opacity, 1))}.bg-red-50{--tw-bg-opacity: 1;background-color:rgb(254 242 242 / var(--tw-bg-opacity, 1))}.bg-rose-100{--tw-bg-opacity: 1;background-color:rgb(255 228 230 / var(--tw-bg-opacity, 1))}.bg-sky-100{--tw-bg-opacity: 1;background-color:rgb(224 242 254 / var(--tw-bg-opacity, 1))}.bg-slate-100{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity, 1))}.bg-slate-200{--tw-bg-opacity: 1;background-color:rgb(226 232 240 / var(--tw-bg-opacity, 1))}.bg-slate-50{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity, 1))}.bg-slate-900{--tw-bg-opacity: 1;background-color:rgb(15 23 42 / var(--tw-bg-opacity, 1))}.bg-teal-100{--tw-bg-opacity: 1;background-color:rgb(204 251 241 / var(--tw-bg-opacity, 1))}.bg-violet-100{--tw-bg-opacity: 1;background-color:rgb(237 233 254 / var(--tw-bg-opacity, 1))}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-white\/80{background-color:#fffc}.object-contain{-o-object-fit:contain;object-fit:contain}.p-0\.5{padding:.125rem}.p-3{padding:.75rem}.p-4{padding:1rem}.px-0{padding-left:0;padding-right:0}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.pb-1{padding-bottom:.25rem}.pb-3{padding-bottom:.75rem}.pb-5{padding-bottom:1.25rem}.pr-10{padding-right:2.5rem}.pr-7{padding-right:1.75rem}.pt-2{padding-top:.5rem}.pt-3{padding-top:.75rem}.pt-5{padding-top:1.25rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.tracking-tight{letter-spacing:-.025em}.tracking-wide{letter-spacing:.025em}.text-amber-300{--tw-text-opacity: 1;color:rgb(252 211 77 / var(--tw-text-opacity, 1))}.text-amber-600{--tw-text-opacity: 1;color:rgb(217 119 6 / var(--tw-text-opacity, 1))}.text-amber-700{--tw-text-opacity: 1;color:rgb(180 83 9 / var(--tw-text-opacity, 1))}.text-blue-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.text-blue-700{--tw-text-opacity: 1;color:rgb(29 78 216 / var(--tw-text-opacity, 1))}.text-emerald-700{--tw-text-opacity: 1;color:rgb(4 120 87 / var(--tw-text-opacity, 1))}.text-orange-600{--tw-text-opacity: 1;color:rgb(234 88 12 / var(--tw-text-opacity, 1))}.text-red-600{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity, 1))}.text-rose-700{--tw-text-opacity: 1;color:rgb(190 18 60 / var(--tw-text-opacity, 1))}.text-sky-700{--tw-text-opacity: 1;color:rgb(3 105 161 / var(--tw-text-opacity, 1))}.text-slate-400{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity, 1))}.text-slate-500{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity, 1))}.text-slate-600{--tw-text-opacity: 1;color:rgb(71 85 105 / var(--tw-text-opacity, 1))}.text-slate-700{--tw-text-opacity: 1;color:rgb(51 65 85 / var(--tw-text-opacity, 1))}.text-slate-800{--tw-text-opacity: 1;color:rgb(30 41 59 / var(--tw-text-opacity, 1))}.text-slate-900{--tw-text-opacity: 1;color:rgb(15 23 42 / var(--tw-text-opacity, 1))}.text-teal-600{--tw-text-opacity: 1;color:rgb(13 148 136 / var(--tw-text-opacity, 1))}.text-violet-700{--tw-text-opacity: 1;color:rgb(109 40 217 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.underline{text-decoration-line:underline}.accent-slate-900{accent-color:#0f172a}.opacity-40{opacity:.4}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}.scrollbar-hide::-webkit-scrollbar{display:none}@media (max-width: 640px){input[type=checkbox]{width:18px;height:18px;min-width:18px}select,input[type=text],input[type=search]{min-height:44px}}.placeholder\:text-slate-400::-moz-placeholder{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity, 1))}.placeholder\:text-slate-400::placeholder{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity, 1))}.last\:border-0:last-child{border-width:0px}.hover\:bg-slate-100:hover{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity, 1))}.hover\:bg-slate-200:hover{--tw-bg-opacity: 1;background-color:rgb(226 232 240 / var(--tw-bg-opacity, 1))}.hover\:bg-slate-50:hover{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity, 1))}.hover\:bg-slate-800:hover{--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity, 1))}.hover\:bg-white:hover{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.hover\:text-slate-600:hover{--tw-text-opacity: 1;color:rgb(71 85 105 / var(--tw-text-opacity, 1))}.hover\:text-slate-900:hover{--tw-text-opacity: 1;color:rgb(15 23 42 / var(--tw-text-opacity, 1))}.hover\:underline:hover{text-decoration-line:underline}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-slate-300:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(203 213 225 / var(--tw-ring-opacity, 1))}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-slate-400\/60:focus-visible{--tw-ring-color: rgb(148 163 184 / .6)}.active\:scale-\[0\.99\]:active{--tw-scale-x: .99;--tw-scale-y: .99;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.active\:brightness-95:active{--tw-brightness: brightness(.95);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.disabled\:opacity-50:disabled{opacity:.5}.dark\:border-slate-600:is(.dark *){--tw-border-opacity: 1;border-color:rgb(71 85 105 / var(--tw-border-opacity, 1))}.dark\:border-slate-700:is(.dark *){--tw-border-opacity: 1;border-color:rgb(51 65 85 / var(--tw-border-opacity, 1))}.dark\:border-slate-800:is(.dark *){--tw-border-opacity: 1;border-color:rgb(30 41 59 / var(--tw-border-opacity, 1))}.dark\:border-slate-900:is(.dark *){--tw-border-opacity: 1;border-color:rgb(15 23 42 / var(--tw-border-opacity, 1))}.dark\:bg-amber-900\/20:is(.dark *){background-color:#78350f33}.dark\:bg-amber-900\/40:is(.dark *){background-color:#78350f66}.dark\:bg-blue-900\/20:is(.dark *){background-color:#1e3a8a33}.dark\:bg-blue-900\/40:is(.dark *){background-color:#1e3a8a66}.dark\:bg-emerald-900\/40:is(.dark *){background-color:#064e3b66}.dark\:bg-orange-900\/40:is(.dark *){background-color:#7c2d1266}.dark\:bg-red-900\/20:is(.dark *){background-color:#7f1d1d33}.dark\:bg-rose-900\/40:is(.dark *){background-color:#88133766}.dark\:bg-sky-900\/40:is(.dark *){background-color:#0c4a6e66}.dark\:bg-slate-100:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity, 1))}.dark\:bg-slate-700:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity, 1))}.dark\:bg-slate-800:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity, 1))}.dark\:bg-slate-800\/60:is(.dark *){background-color:#1e293b99}.dark\:bg-slate-900:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(15 23 42 / var(--tw-bg-opacity, 1))}.dark\:bg-slate-900\/40:is(.dark *){background-color:#0f172a66}.dark\:bg-slate-950:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(2 6 23 / var(--tw-bg-opacity, 1))}.dark\:bg-slate-950\/60:is(.dark *){background-color:#02061799}.dark\:bg-teal-900\/40:is(.dark *){background-color:#134e4a66}.dark\:bg-violet-900\/40:is(.dark *){background-color:#4c1d9566}.dark\:text-amber-300:is(.dark *){--tw-text-opacity: 1;color:rgb(252 211 77 / var(--tw-text-opacity, 1))}.dark\:text-amber-400:is(.dark *){--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.dark\:text-blue-300:is(.dark *){--tw-text-opacity: 1;color:rgb(147 197 253 / var(--tw-text-opacity, 1))}.dark\:text-blue-400:is(.dark *){--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.dark\:text-emerald-300:is(.dark *){--tw-text-opacity: 1;color:rgb(110 231 183 / var(--tw-text-opacity, 1))}.dark\:text-orange-400:is(.dark *){--tw-text-opacity: 1;color:rgb(251 146 60 / var(--tw-text-opacity, 1))}.dark\:text-red-400:is(.dark *){--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.dark\:text-rose-300:is(.dark *){--tw-text-opacity: 1;color:rgb(253 164 175 / var(--tw-text-opacity, 1))}.dark\:text-sky-300:is(.dark *){--tw-text-opacity: 1;color:rgb(125 211 252 / var(--tw-text-opacity, 1))}.dark\:text-slate-100:is(.dark *){--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity, 1))}.dark\:text-slate-200:is(.dark *){--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity, 1))}.dark\:text-slate-300:is(.dark *){--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity, 1))}.dark\:text-slate-400:is(.dark *){--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity, 1))}.dark\:text-slate-500:is(.dark *){--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity, 1))}.dark\:text-slate-900:is(.dark *){--tw-text-opacity: 1;color:rgb(15 23 42 / var(--tw-text-opacity, 1))}.dark\:text-teal-400:is(.dark *){--tw-text-opacity: 1;color:rgb(45 212 191 / var(--tw-text-opacity, 1))}.dark\:text-violet-300:is(.dark *){--tw-text-opacity: 1;color:rgb(196 181 253 / var(--tw-text-opacity, 1))}.dark\:accent-slate-100:is(.dark *){accent-color:#f1f5f9}.dark\:placeholder\:text-slate-500:is(.dark *)::-moz-placeholder{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity, 1))}.dark\:placeholder\:text-slate-500:is(.dark *)::placeholder{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity, 1))}.dark\:hover\:bg-slate-700:hover:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity, 1))}.dark\:hover\:bg-slate-800:hover:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity, 1))}.dark\:hover\:bg-slate-800\/30:hover:is(.dark *){background-color:#1e293b4d}.dark\:hover\:bg-slate-900\/70:hover:is(.dark *){background-color:#0f172ab3}.dark\:hover\:bg-white:hover:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.dark\:hover\:text-slate-200:hover:is(.dark *){--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity, 1))}.dark\:hover\:text-white:hover:is(.dark *){--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.dark\:focus\:ring-slate-700:focus:is(.dark *){--tw-ring-opacity: 1;--tw-ring-color: rgb(51 65 85 / var(--tw-ring-opacity, 1))}.dark\:focus-visible\:ring-slate-500\/50:focus-visible:is(.dark *){--tw-ring-color: rgb(100 116 139 / .5)}@media (min-width: 640px){.sm\:w-auto{width:auto}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:px-4{padding-left:1rem;padding-right:1rem}.sm\:py-2{padding-top:.5rem;padding-bottom:.5rem}.sm\:py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.sm\:py-8{padding-top:2rem;padding-bottom:2rem}} diff --git a/site/assets/telegram-bot.js b/site/assets/telegram-bot.js new file mode 100644 index 0000000..30881ac --- /dev/null +++ b/site/assets/telegram-bot.js @@ -0,0 +1,28 @@ +(function () { + const CARD = + '
' + + '
Telegram-бот для поиска доменов
' + + '

' + + "Если удобнее с телефона или нужен быстрый поиск без браузера — попробуйте " + + '@domain_searchPro_bot. ' + + "Тот же смысл: найти домен, посмотреть IP и выгрузить списки, только в Telegram." + + "

" + + '' + + "Открыть бота →
"; + + function inject() { + if (document.querySelector("[data-telegram-bot-card]")) return; + const host = document.querySelector(".max-w-3xl.mx-auto"); + if (!host) return; + const wrap = document.createElement("div"); + wrap.setAttribute("data-telegram-bot-card", ""); + wrap.className = "mt-4 mb-2"; + wrap.innerHTML = CARD; + host.appendChild(wrap); + } + + const obs = new MutationObserver(() => inject()); + obs.observe(document.body, { childList: true, subtree: true }); + inject(); +})(); diff --git a/site/data/last-update.json b/site/data/last-update.json new file mode 100644 index 0000000..957fb8b --- /dev/null +++ b/site/data/last-update.json @@ -0,0 +1 @@ +{"last_update":"2026-05-24","updating":false,"sources":[{"source_slug":"runetfreedom","source_name":"RuNet Freedom","last_update":"2026-05-24","compatible":"Xray, 3x-ui, V2Ray"},{"source_slug":"loyalsoldier","source_name":"Loyalsoldier","last_update":"2026-05-24","compatible":"Xray, 3x-ui, V2Ray, Mihomo"},{"source_slug":"daniellavrushin","source_name":"DanielLavrushin (b4geoip)","last_update":"2026-05-24","compatible":"Xray, 3x-ui"},{"source_slug":"v2fly","source_name":"v2fly (официальный)","last_update":"2026-05-24","compatible":"Xray, 3x-ui, V2Ray, Sing-box"}]} \ No newline at end of file diff --git a/site/data/presets.json b/site/data/presets.json new file mode 100644 index 0000000..b967cdf --- /dev/null +++ b/site/data/presets.json @@ -0,0 +1 @@ +[{"group":"Мессенджеры","icon":"💬","slug":"messengers","services":[{"name":"Telegram","domain":"telegram.org","keywords":["telegram"],"categories":[{"full_name":"geoip:telegram","name":"telegram","type":"geoip","source_slug":"daniellavrushin","source_name":"DanielLavrushin (b4geoip)"},{"full_name":"geoip:telegram","name":"telegram","type":"geoip","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geoip:telegram","name":"telegram","type":"geoip","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:telegram","name":"telegram","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:telegram","name":"telegram","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:telegram","name":"telegram","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"WhatsApp","domain":"whatsapp.com","keywords":["whatsapp"],"categories":[{"full_name":"geosite:whatsapp","name":"whatsapp","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:whatsapp","name":"whatsapp","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:whatsapp","name":"whatsapp","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Viber","domain":"viber.com","keywords":["viber"],"categories":[{"full_name":"geosite:viber","name":"viber","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:viber","name":"viber","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:viber","name":"viber","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Signal","domain":"signal.org","keywords":["signal"],"categories":[{"full_name":"geosite:signal","name":"signal","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:signal","name":"signal","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:signal","name":"signal","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Discord","domain":"discord.com","keywords":["discord"],"categories":[{"full_name":"geosite:discord","name":"discord","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:discord","name":"discord","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:discord","name":"discord","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Skype","domain":"skype.com","keywords":["skype"],"categories":[{"full_name":"geosite:skyperfect","name":"skyperfect","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:skyperfect","name":"skyperfect","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:skyperfect","name":"skyperfect","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]}]},{"group":"Видео","icon":"🎬","slug":"video","services":[{"name":"YouTube","domain":"youtube.com","keywords":["youtube","googlevideo","ytimg"],"categories":[{"full_name":"geosite:youtube","name":"youtube","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:youtube","name":"youtube","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:youtube","name":"youtube","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Netflix","domain":"netflix.com","keywords":["netflix"],"categories":[{"full_name":"geoip:netflix","name":"netflix","type":"geoip","source_slug":"daniellavrushin","source_name":"DanielLavrushin (b4geoip)"},{"full_name":"geoip:netflix","name":"netflix","type":"geoip","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geoip:netflix","name":"netflix","type":"geoip","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:netflix","name":"netflix","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:netflix","name":"netflix","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:netflix","name":"netflix","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Twitch","domain":"twitch.tv","keywords":["twitch"],"categories":[{"full_name":"geosite:twitch","name":"twitch","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:twitch","name":"twitch","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:twitch","name":"twitch","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"TikTok","domain":"tiktok.com","keywords":["tiktok"],"categories":[{"full_name":"geosite:tiktok","name":"tiktok","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:tiktok","name":"tiktok","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:tiktok","name":"tiktok","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Vimeo","domain":"vimeo.com","keywords":["vimeo"],"categories":[{"full_name":"geosite:vimeo","name":"vimeo","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:vimeo","name":"vimeo","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:vimeo","name":"vimeo","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Rutube","domain":"rutube.ru","keywords":["rutube"],"categories":[{"full_name":"geosite:rutube","name":"rutube","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:rutube","name":"rutube","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:rutube","name":"rutube","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]}]},{"group":"Социальные сети","icon":"🌐","slug":"social","services":[{"name":"Facebook","domain":"facebook.com","keywords":["facebook","meta"],"categories":[{"full_name":"geoip:facebook","name":"facebook","type":"geoip","source_slug":"daniellavrushin","source_name":"DanielLavrushin (b4geoip)"},{"full_name":"geoip:facebook","name":"facebook","type":"geoip","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geoip:facebook","name":"facebook","type":"geoip","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:facebook","name":"facebook","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:facebook","name":"facebook","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:facebook","name":"facebook","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"},{"full_name":"geosite:facebook-dev","name":"facebook-dev","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:facebook-dev","name":"facebook-dev","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:facebook-dev","name":"facebook-dev","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"},{"full_name":"geoip:meta","name":"meta","type":"geoip","source_slug":"daniellavrushin","source_name":"DanielLavrushin (b4geoip)"},{"full_name":"geosite:meta","name":"meta","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:meta","name":"meta","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:meta","name":"meta","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"},{"full_name":"geosite:metabrainz","name":"metabrainz","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:metabrainz","name":"metabrainz","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:metabrainz","name":"metabrainz","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"},{"full_name":"geosite:metacritic","name":"metacritic","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:metacritic","name":"metacritic","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:metacritic","name":"metacritic","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Instagram","domain":"instagram.com","keywords":["instagram"],"categories":[{"full_name":"geosite:instagram","name":"instagram","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:instagram","name":"instagram","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:instagram","name":"instagram","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Twitter/X","domain":"x.com","keywords":["twitter"],"categories":[{"full_name":"geoip:twitter","name":"twitter","type":"geoip","source_slug":"daniellavrushin","source_name":"DanielLavrushin (b4geoip)"},{"full_name":"geoip:twitter","name":"twitter","type":"geoip","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geoip:twitter","name":"twitter","type":"geoip","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:twitter","name":"twitter","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:twitter","name":"twitter","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:twitter","name":"twitter","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"LinkedIn","domain":"linkedin.com","keywords":["linkedin"],"categories":[{"full_name":"geosite:linkedin","name":"linkedin","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:linkedin","name":"linkedin","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:linkedin","name":"linkedin","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Pinterest","domain":"pinterest.com","keywords":["pinterest"],"categories":[{"full_name":"geosite:pinterest","name":"pinterest","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:pinterest","name":"pinterest","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:pinterest","name":"pinterest","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Reddit","domain":"reddit.com","keywords":["reddit"],"categories":[{"full_name":"geosite:reddit","name":"reddit","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:reddit","name":"reddit","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:reddit","name":"reddit","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Tumblr","domain":"tumblr.com","keywords":["tumblr"],"categories":[{"full_name":"geosite:tumblr","name":"tumblr","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:tumblr","name":"tumblr","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:tumblr","name":"tumblr","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]}]},{"group":"Музыка","icon":"🎵","slug":"music","services":[{"name":"Spotify","domain":"spotify.com","keywords":["spotify"],"categories":[{"full_name":"geosite:spotify","name":"spotify","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:spotify","name":"spotify","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:spotify","name":"spotify","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Apple Music","domain":"apple.com","keywords":["apple","itunes"],"categories":[{"full_name":"geoip:apple","name":"apple","type":"geoip","source_slug":"daniellavrushin","source_name":"DanielLavrushin (b4geoip)"},{"full_name":"geosite:apple","name":"apple","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:apple","name":"apple","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:apple","name":"apple","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"},{"full_name":"geosite:apple-cn","name":"apple-cn","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:apple-dev","name":"apple-dev","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:apple-dev","name":"apple-dev","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:apple-dev","name":"apple-dev","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"},{"full_name":"geosite:apple-intelligence","name":"apple-intelligence","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:apple-intelligence","name":"apple-intelligence","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:itunes","name":"itunes","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:itunes","name":"itunes","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:itunes","name":"itunes","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"SoundCloud","domain":"soundcloud.com","keywords":["soundcloud"],"categories":[{"full_name":"geosite:soundcloud","name":"soundcloud","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:soundcloud","name":"soundcloud","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:soundcloud","name":"soundcloud","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Deezer","domain":"deezer.com","keywords":["deezer"],"categories":[{"full_name":"geosite:deezer","name":"deezer","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:deezer","name":"deezer","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:deezer","name":"deezer","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Last.fm","domain":"last.fm","keywords":["lastfm"],"categories":[{"full_name":"geosite:lastfm","name":"lastfm","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:lastfm","name":"lastfm","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:lastfm","name":"lastfm","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]}]},{"group":"Искусственный интеллект","icon":"🤖","slug":"ai","services":[{"name":"ChatGPT","domain":"openai.com","keywords":["openai","chatgpt"],"categories":[{"full_name":"geosite:openai","name":"openai","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:openai","name":"openai","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:openai","name":"openai","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Claude","domain":"anthropic.com","keywords":["claude","anthropic"],"categories":[{"full_name":"geoip:anthropic","name":"anthropic","type":"geoip","source_slug":"daniellavrushin","source_name":"DanielLavrushin (b4geoip)"},{"full_name":"geosite:anthropic","name":"anthropic","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:anthropic","name":"anthropic","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:anthropic","name":"anthropic","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Gemini","domain":"gemini.google.com","keywords":["gemini","bard"],"categories":[{"full_name":"geosite:google-gemini","name":"google-gemini","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:google-gemini","name":"google-gemini","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:google-gemini","name":"google-gemini","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Midjourney","domain":"midjourney.com","keywords":["midjourney"],"categories":[]},{"name":"Hugging Face","domain":"huggingface.co","keywords":["huggingface"],"categories":[{"full_name":"geosite:huggingface","name":"huggingface","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:huggingface","name":"huggingface","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:huggingface","name":"huggingface","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Perplexity","domain":"perplexity.ai","keywords":["perplexity"],"categories":[{"full_name":"geosite:perplexity","name":"perplexity","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:perplexity","name":"perplexity","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:perplexity","name":"perplexity","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Stability AI","domain":"stability.ai","keywords":["stability"],"categories":[]}]},{"group":"Игры","icon":"🎮","slug":"games","services":[{"name":"Steam","domain":"store.steampowered.com","keywords":["steam","steamcontent"],"categories":[{"full_name":"geosite:steam","name":"steam","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:steam","name":"steam","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:steam","name":"steam","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"},{"full_name":"geosite:steaminventoryhelper","name":"steaminventoryhelper","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:steaminventoryhelper","name":"steaminventoryhelper","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:steaminventoryhelper","name":"steaminventoryhelper","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"},{"full_name":"geosite:steamunlocked","name":"steamunlocked","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:steamunlocked","name":"steamunlocked","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:steamunlocked","name":"steamunlocked","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Epic Games","domain":"epicgames.com","keywords":["epicgames","epic"],"categories":[{"full_name":"geoip:epicgames","name":"epicgames","type":"geoip","source_slug":"daniellavrushin","source_name":"DanielLavrushin (b4geoip)"},{"full_name":"geosite:epicgames","name":"epicgames","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:epicgames","name":"epicgames","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:epicgames","name":"epicgames","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"},{"full_name":"geosite:epicbrowser","name":"epicbrowser","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:epicbrowser","name":"epicbrowser","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:epicbrowser","name":"epicbrowser","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"PlayStation","domain":"playstation.com","keywords":["playstation","psn"],"categories":[{"full_name":"geosite:playstation","name":"playstation","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:playstation","name":"playstation","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:playstation","name":"playstation","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Xbox","domain":"xbox.com","keywords":["xbox","xboxlive"],"categories":[{"full_name":"geosite:xbox","name":"xbox","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:xbox","name":"xbox","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:xbox","name":"xbox","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"EA / Origin","domain":"ea.com","keywords":["ea.com","origin","easports"],"categories":[{"full_name":"geosite:origin","name":"origin","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:origin","name":"origin","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:origin","name":"origin","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Blizzard","domain":"blizzard.com","keywords":["blizzard","battle.net"],"categories":[{"full_name":"geoip:blizzard","name":"blizzard","type":"geoip","source_slug":"daniellavrushin","source_name":"DanielLavrushin (b4geoip)"},{"full_name":"geosite:blizzard","name":"blizzard","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:blizzard","name":"blizzard","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:blizzard","name":"blizzard","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Riot Games","domain":"riotgames.com","keywords":["riotgames","valorant"],"categories":[]},{"name":"Roblox","domain":"roblox.com","keywords":["roblox"],"categories":[{"full_name":"geoip:roblox","name":"roblox","type":"geoip","source_slug":"daniellavrushin","source_name":"DanielLavrushin (b4geoip)"},{"full_name":"geosite:roblox","name":"roblox","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:roblox","name":"roblox","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:roblox","name":"roblox","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Minecraft","domain":"minecraft.net","keywords":["minecraft","mojang"],"categories":[{"full_name":"geosite:mojang","name":"mojang","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:mojang","name":"mojang","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:mojang","name":"mojang","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"GOG","domain":"gog.com","keywords":["gog"],"categories":[{"full_name":"geosite:gog","name":"gog","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:gog","name":"gog","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:gog","name":"gog","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]}]},{"group":"Работа и инструменты","icon":"💼","slug":"work","services":[{"name":"Google","domain":"google.com","keywords":["google","googleapis"],"categories":[{"full_name":"geoip:google","name":"google","type":"geoip","source_slug":"daniellavrushin","source_name":"DanielLavrushin (b4geoip)"},{"full_name":"geoip:google","name":"google","type":"geoip","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geoip:google","name":"google","type":"geoip","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:google","name":"google","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:google","name":"google","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:google","name":"google","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"},{"full_name":"geosite:google-cn","name":"google-cn","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:google-deepmind","name":"google-deepmind","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:google-deepmind","name":"google-deepmind","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:google-deepmind","name":"google-deepmind","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Microsoft","domain":"microsoft.com","keywords":["microsoft","office365"],"categories":[{"full_name":"geosite:microsoft","name":"microsoft","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:microsoft","name":"microsoft","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:microsoft","name":"microsoft","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"},{"full_name":"geosite:microsoft-dev","name":"microsoft-dev","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:microsoft-dev","name":"microsoft-dev","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:microsoft-dev","name":"microsoft-dev","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"},{"full_name":"geosite:microsoft-pki","name":"microsoft-pki","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:microsoft-pki","name":"microsoft-pki","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:microsoft-pki","name":"microsoft-pki","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Zoom","domain":"zoom.us","keywords":["zoom"],"categories":[{"full_name":"geosite:zoom","name":"zoom","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:zoom","name":"zoom","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:zoom","name":"zoom","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Slack","domain":"slack.com","keywords":["slack"],"categories":[{"full_name":"geosite:slack","name":"slack","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:slack","name":"slack","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:slack","name":"slack","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Notion","domain":"notion.so","keywords":["notion"],"categories":[{"full_name":"geosite:notion","name":"notion","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:notion","name":"notion","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:notion","name":"notion","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Figma","domain":"figma.com","keywords":["figma"],"categories":[{"full_name":"geosite:figma","name":"figma","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:figma","name":"figma","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:figma","name":"figma","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"GitHub","domain":"github.com","keywords":["github","githubusercontent"],"categories":[{"full_name":"geoip:github","name":"github","type":"geoip","source_slug":"daniellavrushin","source_name":"DanielLavrushin (b4geoip)"},{"full_name":"geosite:github","name":"github","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:github","name":"github","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:github","name":"github","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"},{"full_name":"geosite:github-copilot","name":"github-copilot","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:github-copilot","name":"github-copilot","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:github-copilot","name":"github-copilot","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"},{"full_name":"geosite:github1s","name":"github1s","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:github1s","name":"github1s","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:github1s","name":"github1s","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"AnyDesk","domain":"anydesk.com","keywords":["anydesk"],"categories":[]},{"name":"TeamViewer","domain":"teamviewer.com","keywords":["teamviewer"],"categories":[{"full_name":"geosite:teamviewer","name":"teamviewer","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:teamviewer","name":"teamviewer","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:teamviewer","name":"teamviewer","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Atlassian","domain":"atlassian.com","keywords":["atlassian","jira"],"categories":[{"full_name":"geosite:atlassian","name":"atlassian","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:atlassian","name":"atlassian","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:atlassian","name":"atlassian","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]}]},{"group":"CDN и инфраструктура","icon":"☁️","slug":"cdn","services":[{"name":"Cloudflare","domain":"cloudflare.com","keywords":["cloudflare"],"categories":[{"full_name":"geoip:cloudflare","name":"cloudflare","type":"geoip","source_slug":"daniellavrushin","source_name":"DanielLavrushin (b4geoip)"},{"full_name":"geoip:cloudflare","name":"cloudflare","type":"geoip","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geoip:cloudflare","name":"cloudflare","type":"geoip","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:cloudflare","name":"cloudflare","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:cloudflare","name":"cloudflare","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:cloudflare","name":"cloudflare","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"},{"full_name":"geosite:cloudflare-cn","name":"cloudflare-cn","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:cloudflare-cn","name":"cloudflare-cn","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:cloudflare-cn","name":"cloudflare-cn","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"},{"full_name":"geosite:cloudflare-ipfs","name":"cloudflare-ipfs","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"}]},{"name":"Akamai","domain":"akamai.com","keywords":["akamai"],"categories":[{"full_name":"geoip:akamai","name":"akamai","type":"geoip","source_slug":"daniellavrushin","source_name":"DanielLavrushin (b4geoip)"},{"full_name":"geosite:akamai","name":"akamai","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:akamai","name":"akamai","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:akamai","name":"akamai","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Fastly","domain":"fastly.com","keywords":["fastly"],"categories":[{"full_name":"geoip:fastly","name":"fastly","type":"geoip","source_slug":"daniellavrushin","source_name":"DanielLavrushin (b4geoip)"},{"full_name":"geoip:fastly","name":"fastly","type":"geoip","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geoip:fastly","name":"fastly","type":"geoip","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:fastly","name":"fastly","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:fastly","name":"fastly","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:fastly","name":"fastly","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Amazon AWS","domain":"aws.amazon.com","keywords":["amazon","amazonaws"],"categories":[{"full_name":"geoip:amazon","name":"amazon","type":"geoip","source_slug":"daniellavrushin","source_name":"DanielLavrushin (b4geoip)"},{"full_name":"geosite:amazon","name":"amazon","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:amazon","name":"amazon","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:amazon","name":"amazon","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"},{"full_name":"geosite:amazontrust","name":"amazontrust","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:amazontrust","name":"amazontrust","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:amazontrust","name":"amazontrust","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"}]},{"name":"Google Cloud","domain":"cloud.google.com","keywords":["googleusercontent","gstatic"],"categories":[]}]},{"group":"Заблокировано в РФ","icon":"🛡️","slug":"blocked_ru","services":[{"name":"Список РКН","domain":"rkn.gov.ru","keywords":["ru-blocked","rkn","russia-blocked"],"categories":[{"full_name":"geoip:ru-blocked","name":"ru-blocked","type":"geoip","source_slug":"daniellavrushin","source_name":"DanielLavrushin (b4geoip)"},{"full_name":"geoip:ru-blocked","name":"ru-blocked","type":"geoip","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geoip:ru-blocked-community","name":"ru-blocked-community","type":"geoip","source_slug":"daniellavrushin","source_name":"DanielLavrushin (b4geoip)"},{"full_name":"geoip:ru-blocked-community","name":"ru-blocked-community","type":"geoip","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:category-media-ru-blocked","name":"category-media-ru-blocked","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:category-media-ru-blocked","name":"category-media-ru-blocked","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:category-media-ru-blocked","name":"category-media-ru-blocked","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"},{"full_name":"geosite:ru-blocked","name":"ru-blocked","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:ru-blocked-all","name":"ru-blocked-all","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"}]},{"name":"Антифильтр","domain":"antifilter.download","keywords":["antifilter"],"categories":[{"full_name":"geosite:antifilter-download","name":"antifilter-download","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:antifilter-download-community","name":"antifilter-download-community","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"}]},{"name":"Заблокировано","domain":"geoexport.org","keywords":["blocked"],"categories":[{"full_name":"geoip:ru-blocked","name":"ru-blocked","type":"geoip","source_slug":"daniellavrushin","source_name":"DanielLavrushin (b4geoip)"},{"full_name":"geoip:ru-blocked","name":"ru-blocked","type":"geoip","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geoip:ru-blocked-community","name":"ru-blocked-community","type":"geoip","source_slug":"daniellavrushin","source_name":"DanielLavrushin (b4geoip)"},{"full_name":"geoip:ru-blocked-community","name":"ru-blocked-community","type":"geoip","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:category-media-ru-blocked","name":"category-media-ru-blocked","type":"geosite","source_slug":"loyalsoldier","source_name":"Loyalsoldier"},{"full_name":"geosite:category-media-ru-blocked","name":"category-media-ru-blocked","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:category-media-ru-blocked","name":"category-media-ru-blocked","type":"geosite","source_slug":"v2fly","source_name":"v2fly (официальный)"},{"full_name":"geosite:ru-blocked","name":"ru-blocked","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"},{"full_name":"geosite:ru-blocked-all","name":"ru-blocked-all","type":"geosite","source_slug":"runetfreedom","source_name":"RuNet Freedom"}]}]}] \ No newline at end of file diff --git a/site/data/routing-presets.json b/site/data/routing-presets.json new file mode 100644 index 0000000..c83bde0 --- /dev/null +++ b/site/data/routing-presets.json @@ -0,0 +1 @@ +{"presets":{"direct_sites":[{"id":"ru_sites","label":"Российские сайты","description":"Домены .ru, .рф и российские сервисы — идут напрямую без VPN","value":"geosite:category-ru","checked":true},{"id":"ru_whitelist","label":"Белый список РФ","description":"Дополнительный список российских сервисов и госсайтов","value":"geosite:whitelist","checked":true},{"id":"private","label":"Локальная сеть","description":"192.168.x.x, 10.x.x.x и другие приватные адреса — всегда напрямую","value":"geosite:private","checked":true},{"id":"apple","label":"Apple","description":"iCloud, App Store, Apple Maps — для корректной работы устройства","value":"geosite:apple","checked":true},{"id":"microsoft","label":"Microsoft","description":"Windows Update, Office, Teams — экономия трафика VPN","value":"geosite:microsoft","checked":false},{"id":"steam","label":"Steam","description":"Игры и обновления Steam — напрямую для скорости","value":"geosite:steam","checked":false},{"id":"google_cn","label":"Google (доступные сервисы)","description":"Сервисы Google доступные в РФ без VPN","value":"geosite:google@cn","checked":false}],"direct_ips":[{"id":"geoip_ru","label":"IP-адреса России","description":"Все российские IP-диапазоны — напрямую","value":"geoip:ru","checked":true},{"id":"geoip_private","label":"Приватные IP","description":"10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16","value":"geoip:private","checked":true}],"proxy_sites":[{"id":"youtube","label":"YouTube","description":"YouTube заблокирован в РФ — направляем через VPN","value":"geosite:youtube","checked":true},{"id":"telegram","label":"Telegram","description":"Серверы Telegram — через VPN для надёжности","value":"geosite:telegram","checked":true},{"id":"meta","label":"Meta (Instagram, Facebook)","description":"Instagram и Facebook заблокированы в РФ","value":"geosite:facebook","checked":true},{"id":"twitter","label":"Twitter / X","description":"Twitter заблокирован в РФ","value":"geosite:twitter","checked":true},{"id":"netflix","label":"Netflix","description":"Netflix недоступен в РФ","value":"geosite:netflix","checked":false},{"id":"spotify","label":"Spotify","description":"Spotify недоступен в РФ","value":"geosite:spotify","checked":false},{"id":"github","label":"GitHub","description":"GitHub периодически блокируется в РФ","value":"geosite:github","checked":false},{"id":"geolocation_notcn","label":"Все заблокированные (универсальный)","description":"Весь иностранный трафик не из РФ/CN — через VPN. Осторожно: большой список","value":"geosite:geolocation-!cn","checked":false}],"proxy_ips":[{"id":"geoip_telegram","label":"IP Telegram","description":"IP-диапазоны серверов Telegram","value":"geoip:telegram","checked":true}],"block_sites":[{"id":"ads","label":"Реклама","description":"Рекламные домены — блокируем полностью","value":"geosite:category-ads-all","checked":true}],"block_ips":[]},"geo_sources":{"loyalsoldier":{"label":"Loyalsoldier (рекомендуется)","geoip":"https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat","geosite":"https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat"},"runetfreedom":{"label":"RuNet Freedom (РКН + Россия)","geoip":"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geoip.dat","geosite":"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geosite.dat"},"daniellavrushin":{"label":"DanielLavrushin (расширенная)","geoip":"https://github.com/DanielLavrushin/b4geoip/releases/latest/download/geoip.dat","geosite":"https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat"},"v2fly":{"label":"v2fly (оригинальная)","geoip":"https://github.com/v2fly/geoip/releases/latest/download/geoip.dat","geosite":"https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat"}},"route_orders":[{"value":"block-direct-proxy","label":"Блок → Прямой → VPN (рекомендуется)"},{"value":"block-proxy-direct","label":"Блок → VPN → Прямой"},{"value":"proxy-direct-block","label":"VPN → Прямой → Блок"}],"dns_types":[{"value":"DoH","label":"DoH (DNS over HTTPS, рекомендуется)"},{"value":"DoU","label":"DoU (DNS over UDP, быстрее)"}]} \ No newline at end of file diff --git a/site/data/sources.json b/site/data/sources.json new file mode 100644 index 0000000..a1e7667 --- /dev/null +++ b/site/data/sources.json @@ -0,0 +1 @@ +[{"slug":"runetfreedom","name":"RuNet Freedom","description":"Российские правила блокировок, оптимизировано для обхода РКН","compatible":"Xray, 3x-ui, V2Ray","geoip_url":"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geoip.dat","geosite_url":"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geosite.dat"},{"slug":"loyalsoldier","name":"Loyalsoldier","description":"Популярный набор правил, стандарт для большинства клиентов","compatible":"Xray, 3x-ui, V2Ray, Mihomo","geoip_url":"https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat","geosite_url":"https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat"},{"slug":"daniellavrushin","name":"DanielLavrushin (b4geoip)","description":"Расширенная база с дополнительными категориями (Akamai, CDN и др.)","compatible":"Xray, 3x-ui","geoip_url":"https://github.com/DanielLavrushin/b4geoip/releases/latest/download/geoip.dat","geosite_url":null},{"slug":"v2fly","name":"v2fly (официальный)","description":"Официальные базы проекта v2fly/domain-list-community","compatible":"Xray, 3x-ui, V2Ray, Sing-box","geoip_url":"https://github.com/v2fly/geoip/releases/latest/download/geoip.dat","geosite_url":"https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat"}] \ No newline at end of file diff --git a/site/index.html b/site/index.html new file mode 100644 index 0000000..a26fe7b --- /dev/null +++ b/site/index.html @@ -0,0 +1,14 @@ + + + + + + GeoExport + + + + +
+ + + diff --git a/site/package.json b/site/package.json new file mode 100644 index 0000000..8e5ce0b --- /dev/null +++ b/site/package.json @@ -0,0 +1,15 @@ +{ + "name": "geoexport-clone", + "private": true, + "version": "1.0.0", + "description": "Local mirror of https://geoexport.org/", + "scripts": { + "start": "node server.mjs", + "dev": "node server.mjs", + "patch": "node patch-bundle.mjs", + "prestart": "node patch-bundle.mjs" + }, + "engines": { + "node": ">=18" + } +} diff --git a/site/patch-bundle.mjs b/site/patch-bundle.mjs new file mode 100644 index 0000000..b6d2150 --- /dev/null +++ b/site/patch-bundle.mjs @@ -0,0 +1,53 @@ +#!/usr/bin/env node +/** + * Patches GeoExport SPA bundle: export preview/download fixes + shared fetch helper. + */ +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const bundlePath = path.join(__dirname, "assets", "index-VQj200iM.js"); + +const HELPER = `async function Ge(cats,type,fmt,source,label){const w=new URLSearchParams;cats.forEach(z=>w.append("category",z)),w.set("type",type);const af=fmt==="single-line"||fmt==="keenetic-ip"?"plain-lines":fmt;w.set("format",af),source&&w.set("source",source);const r=await fetch(\`/api/export?\${w}\`);if(!r.ok){const e=await r.json().catch(()=>({}));throw new Error(e.detail||r.statusText||"Ошибка экспорта")}let t=await r.text();if(!t.trim())return"Нет данных для выбранных категорий, типа или источника. Попробуйте «Все» в фильтре источников или тип «Домены» / «IPv4».";if(fmt==="single-line")t=t.trim().split(/\\r?\\n/).join(",");if(fmt==="keenetic-ip"){const lines=t.trim().split(/\\r?\\n/).filter(x=>/^\\d/.test(x)&&x.includes("/")&&!x.includes(":"));t=lines.length?lines.map(line=>{const[ip,bits]=line.split("/"),n=parseInt(bits??"32",10),mask=n===32?"255.255.255.255":Array.from({length:4},(_,i)=>(65280>>Math.min(8,Math.max(0,n-i*8)))&255).join(".");return\`route ADD \${ip} MASK \${mask} 0.0.0.0 & rem \${label||cats[0]||"export"}\`}).join("\\n"):"Нет IPv4-подсетей для Keenetic. Выберите тип «IPv4» или «Домены + IPv4»."}return t}`; + +const REPLACEMENTS = [ + { + from: "function bm({svc:e,sources:t,sourceFilter:n})", + to: `${HELPER}function bm({svc:e,sources:t,sourceFilter:n})`, + }, + { + from: 'p=async()=>{if(f){a(!0);try{const w=new URLSearchParams;C.forEach(z=>w.append("category",z.full_name)),w.set("type",m),w.set("format",c==="single-line"?"plain-lines":c),n!=="all"&&w.set("source",n);let P=await(await fetch(`/api/export?${w}`)).text();c==="single-line"&&(P=P.trim().split(/\\r?\\n/).join(",")),o(P)}finally{a(!1)}}}', + to: 'p=async()=>{if(f){a(!0);try{o(await Ge(C.map(z=>z.full_name),m,c,n!=="all"?n:"",e.name))}catch(w){o(w.message||"Ошибка экспорта")}finally{a(!1)}}}', + }, + { + from: 'function qm(){const[e]=hi(),t=e.getAll("category"),n=e.get("source"),[r,l]=k.useState("domains,ip4,ip6"),[s,o]=k.useState("plain-lines"),', + to: 'function qm(){const[e]=hi(),t=e.getAll("category"),n=e.get("source"),[r,l]=k.useState(e.get("type")||"domains,ip4,ip6"),[s,o]=k.useState(e.get("format")||"plain-lines"),', + }, + { + from: 'const p=k.useCallback(async()=>{if(t.length){y(!0),C(null);try{const S=new URLSearchParams;t.forEach(T=>S.append("category",T)),S.set("type",r),S.set("format",s==="single-line"?"plain-lines":s),n&&S.set("source",n);const P=await fetch(`/api/export?${S}`);if(!P.ok){const T=await P.json().catch(()=>({}));C(T.detail??P.statusText),a(""),h(0);return}let z=await P.text();s==="single-line"&&(z=z.trim().split(/\\r?\\n/).join(",")),a(z),h(z.trim()?z.trim().split(/[,\\r?\\n]/).filter(Boolean).length:0)}finally{y(!1)}}},[t.join(","),r,s,n]);', + to: 'const p=k.useCallback(async()=>{if(!t.length){a(""),C(null),h(0);return}y(!0),C(null);try{const z=await Ge(t,r,s,n||"",t[0]);a(z),h(z.trim()&&!z.startsWith("Нет ")?z.trim().split(/[,\\r?\\n]/).filter(Boolean).length:0)}catch(S){C(S.message||"Ошибка экспорта"),a("") ,h(0)}finally{y(!1)}},[t.join(","),r,s,n]);', + }, + { + from: 'value:g?"Загрузка…":j?"":i,readOnly:!0,className:"w-full h-80', + to: 'value:g?"Загрузка…":j||i||(!t.length?"Выберите категории на главной и нажмите «Открыть экспорт →», либо добавьте ?category=geoip:telegram в адрес.":""),readOnly:!0,className:"w-full h-80', + }, +]; + +let src = fs.readFileSync(bundlePath, "utf8"); + +if (src.includes("async function Ge(")) { + console.log("Already patched:", bundlePath); + process.exit(0); +} + +for (const { from, to } of REPLACEMENTS) { + if (!src.includes(from)) { + console.error("Patch anchor not found:", from.slice(0, 80)); + process.exit(1); + } + src = src.replace(from, to); +} + +fs.writeFileSync(bundlePath, src); +console.log("Patched", bundlePath); diff --git a/site/server.mjs b/site/server.mjs new file mode 100644 index 0000000..69679d1 --- /dev/null +++ b/site/server.mjs @@ -0,0 +1,146 @@ +#!/usr/bin/env node +/** + * Local GeoExport mirror: static assets + cached JSON + proxy for dynamic API. + */ +import http from "node:http"; +import https from "node:https"; +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const PORT = Number(process.env.PORT) || 4173; +const UPSTREAM = "geoexport.org"; + +const LOCAL_API = new Set([ + "/api/presets", + "/api/sources", + "/api/last-update", + "/api/routing/presets", +]); + +const DATA_MAP = { + "/api/presets": "presets.json", + "/api/sources": "sources.json", + "/api/last-update": "last-update.json", + "/api/routing/presets": "routing-presets.json", +}; + +const MIME = { + ".html": "text/html; charset=utf-8", + ".js": "text/javascript; charset=utf-8", + ".css": "text/css; charset=utf-8", + ".json": "application/json; charset=utf-8", + ".ico": "image/x-icon", + ".svg": "image/svg+xml", + ".png": "image/png", +}; + +function send(res, status, body, headers = {}) { + res.writeHead(status, headers); + res.end(body); +} + +function serveFile(res, filePath) { + const ext = path.extname(filePath); + const type = MIME[ext] || "application/octet-stream"; + fs.readFile(filePath, (err, data) => { + if (err) { + send(res, 404, "Not found"); + return; + } + send(res, 200, data, { "Content-Type": type }); + }); +} + +function serveLocalApi(res, pathname) { + const file = DATA_MAP[pathname]; + if (!file) { + send(res, 404, JSON.stringify({ error: "unknown local api" }), { + "Content-Type": "application/json", + }); + return; + } + const full = path.join(__dirname, "data", file); + fs.readFile(full, (err, data) => { + if (err) { + send(res, 500, JSON.stringify({ error: err.message }), { + "Content-Type": "application/json", + }); + return; + } + send(res, 200, data, { + "Content-Type": "application/json; charset=utf-8", + "Cache-Control": "no-cache", + }); + }); +} + +function proxyToUpstream(req, res) { + const options = { + hostname: UPSTREAM, + port: 443, + path: req.url, + method: req.method, + headers: { + ...req.headers, + host: UPSTREAM, + }, + }; + + const proxyReq = https.request(options, (proxyRes) => { + const headers = { ...proxyRes.headers }; + delete headers["content-security-policy"]; + res.writeHead(proxyRes.statusCode || 502, headers); + proxyRes.pipe(res); + }); + + proxyReq.on("error", (err) => { + send(res, 502, JSON.stringify({ error: `Upstream error: ${err.message}` }), { + "Content-Type": "application/json", + }); + }); + + req.pipe(proxyReq); +} + +function handler(req, res) { + const url = new URL(req.url, `http://127.0.0.1:${PORT}`); + let pathname = decodeURIComponent(url.pathname); + + if (pathname.startsWith("/api/")) { + if (LOCAL_API.has(pathname)) { + serveLocalApi(res, pathname); + return; + } + proxyToUpstream(req, res); + return; + } + + if (pathname === "/") pathname = "/index.html"; + + const safe = path.normalize(pathname).replace(/^(\.\.[/\\])+/, ""); + const filePath = path.join(__dirname, safe); + + if (!filePath.startsWith(__dirname)) { + send(res, 403, "Forbidden"); + return; + } + + fs.stat(filePath, (err, stat) => { + if (err || !stat.isFile()) { + if (!path.extname(pathname)) { + serveFile(res, path.join(__dirname, "index.html")); + return; + } + send(res, 404, "Not found"); + return; + } + serveFile(res, filePath); + }); +} + +http.createServer(handler).listen(PORT, () => { + console.log(`GeoExport clone: http://127.0.0.1:${PORT}`); + console.log(`Static + local JSON; export/search/lookup proxied to ${UPSTREAM}`); +});