diff --git a/src/lib/common/services/network.service.js b/src/lib/common/services/network.service.js new file mode 100644 index 0000000..fe7efe1 --- /dev/null +++ b/src/lib/common/services/network.service.js @@ -0,0 +1,69 @@ +// lib/common/services/network.service.js +// +// Определение сети, в которой находится устройство. +// +// - внутри локальной сети принтера (Wi-Fi UnitPrintLocal) +// домен unitprint.ru резолвится на 192.168.20.1 — локальный сервер печати; +// - из интернета домен отвечает публичным адресом 95.165.135.233. +// +// Скан работает только через интернет, печать — только через локальную сеть. + +export const DOMAIN = 'unitprint.ru'; +export const PUBLIC_IP = '95.165.135.233'; +export const LOCAL_IP = '192.168.20.1'; +export const LOCAL_WIFI_NAME = 'UnitPrintLocal'; + +/** + * Сетевая доступность URL. + * mode: 'no-cors' — чтобы CORS не мешал: важен сам факт ответа сервера, + * а не содержимое ответа. Любой HTTP-ответ = хост доступен. + */ +async function canReach(url, timeoutMs = 4000) { + if (typeof fetch === 'undefined') return false; + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), timeoutMs); + try { + await fetch(url, { + method: 'GET', + mode: 'no-cors', + cache: 'no-store', + signal: controller.signal, + }); + return true; + } catch { + return false; + } finally { + clearTimeout(timer); + } +} + +/** Доступен ли локальный сервер печати (192.168.20.1) */ +export async function isLocalServerReachable() { + const ts = Date.now(); + if (await canReach(`http://${LOCAL_IP}/api/version?ping=${ts}`, 3000)) return true; + return canReach(`https://${LOCAL_IP}/api/version?ping=${ts}`, 3000); +} + +/** Есть ли интернет (домен отвечает публичным сервером) */ +export function isInternetReachable() { + return canReach(`https://${DOMAIN}/api/version?ping=${Date.now()}`, 5000); +} + +/** + * Определяет текущий режим сети. + * @returns {Promise<'local' | 'internet' | 'offline'>} + * - 'local' → устройство в сети принтера (домен → 192.168.20.1) + * - 'internet' → устройство в интернете (домен → 95.165.135.233) + * - 'offline' → нет ни того, ни другого + */ +export async function detectNetwork() { + const [local, internet] = await Promise.all([ + isLocalServerReachable(), + isInternetReachable(), + ]); + // Локальная сеть в приоритете: если 192.168.20.1 доступен, + // значит домен сейчас указывает на локальный сервер печати. + if (local) return 'local'; + if (internet) return 'internet'; + return 'offline'; +} diff --git a/src/lib/common/ui/NetworkGate.svelte b/src/lib/common/ui/NetworkGate.svelte new file mode 100644 index 0000000..e64e467 --- /dev/null +++ b/src/lib/common/ui/NetworkGate.svelte @@ -0,0 +1,444 @@ + + + +
+
+ +

{isLocalMode ? 'Подключение к принтеру' : 'Нет интернета'}

+
+ +
+ {#if status === 'checking'} +
+
+

Проверка подключения…

+

Определяем, в какой вы сети

+
+ {:else if status === 'success'} +
+
+ +
+

{isLocalMode ? 'Принтер найден' : 'Интернет подключен'}

+

Продолжаем…

+
+ {:else} +
+ {#if isLocalMode} + + {:else} + + {/if} +
+ +
+ {#if isLocalMode} +

Нет связи с принтером

+

Печать доступна только в локальной сети принтера. Подключите устройство к сети:

+ {:else} +

Необходимо подключение к интернету

+

Убедитесь, что вы отключены от локальной сети Wi-Fi принтера.

+ {/if} +
+ +
+ {#if isLocalMode} +
+ 1 +
+ Подключитесь к Wi-Fi +
+ + + {LOCAL_WIFI_NAME} + + +
+
+
+
+ 2 + Отключите мобильный интернет +
+
+ 3 + Отключите VPN +
+ {:else} +
+ 1 + Отключитесь от сети {LOCAL_WIFI_NAME} +
+
+ 2 + Включите мобильный интернет или подключитесь к другому Wi-Fi +
+ {/if} +
+ +
+
+ {isLocalMode ? 'Ожидание подключения к принтеру…' : 'Ожидание интернета…'} +
+

Как только подключение появится — продолжим автоматически

+ + + {/if} +
+
+ + diff --git a/src/lib/pages/main/MainMenu.svelte b/src/lib/pages/main/MainMenu.svelte index ec6b923..7b6e884 100644 --- a/src/lib/pages/main/MainMenu.svelte +++ b/src/lib/pages/main/MainMenu.svelte @@ -205,5 +205,5 @@
v{backendVersion} - v1.0.7 + v1.0.8
diff --git a/src/lib/pages/print/PrintMenu.svelte b/src/lib/pages/print/PrintMenu.svelte index df0055a..e0d9863 100644 --- a/src/lib/pages/print/PrintMenu.svelte +++ b/src/lib/pages/print/PrintMenu.svelte @@ -6,6 +6,8 @@ import PrintProgress from './PrintProgress.svelte'; import PaymentPage from '$lib/common/ui/PaymentPage.svelte'; import { plFiles, plCopies } from '$lib/common/utils/pricing.util.js'; + import NetworkGate from '$lib/common/ui/NetworkGate.svelte'; + import { detectNetwork } from '$lib/common/services/network.service.js'; let { onBack = () => {} } = $props(); const store = createPrintStore(); @@ -33,18 +35,59 @@ return f.sides !== 'Двусторонняя' && s >= 25; }) ); + + /* ── Проверка сети перед отправкой печати ── + * Печать работает только в локальной сети принтера (домен → 192.168.20.1). + * Если «Я оплатил(а)» нажата не в той сети — показываем экран подключения + * и автоматически отправляем печать, как только сеть появится. + */ + /** @type {null | 'checking' | 'blocked'} */ + let printNetGate = $state(null); + + async function handleConfirmPayment() { + if (printNetGate === 'checking') return; + printNetGate = 'checking'; + const net = await detectNetwork(); + if (net === 'local') { + printNetGate = null; + store.submitPrint(); + } else { + printNetGate = 'blocked'; + } + } + + function handleGateReady() { + // сеть принтера появилась — пропускаем пользователя и сразу печатаем + printNetGate = null; + store.submitPrint(); + } + + function handleGateBack() { + printNetGate = null; // возврат к странице оплаты + } {#if store.job} {:else if store.showPayment} - + {#if printNetGate === 'blocked'} + + {:else if printNetGate === 'checking'} +
+
+
+

Проверка подключения к принтеру…

+
+
+ {:else} + + {/if} {:else if !store.activeFile}
@@ -99,6 +142,9 @@ {/if}