diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ab18a4a --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +# Local credentials, runtime dependencies and backups +.deps/ +__pycache__/ +release-backups/ +*.zip +*.key +*.pem +*.crt +*.env +*.secret +*.secret.* +*id_rsa* +verification.json +live-verification.json +reference/live-* +artwork/photos/ +artwork/**/contact-sheet.jpg +gitea_auth_local.py +prepare_gitea_tree.py diff --git a/README.md b/README.md index 633a1dc..5317423 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,17 @@ # servaki.online +## Версия сайта от 28 сентября 2026 + +Здесь сохранены исходники, оригиналы изображений и готовый каталог `public/`, +который опубликован на Beget. Главная связывает 12 карточек с профильными статьями; +есть 3 каталога, адаптивные изображения, лёгкие схемы работы оборудования и SEO-разметка. +Состав сборки, проверки, доступ к хостингу без секретов и порядок отката — в [SITE-BUILD.md](SITE-BUILD.md). + +Актуальные тарифы: «Старт» — 9 000 ₽/мес., «Бизнес» — 21 000 ₽/мес., +«Премиум» — 45 000 ₽/мес. Публикация на Beget выполняется отдельно от Git. + +## Сведения о прежней версии + **Сайт:** https://servaki.online **Назначение:** IT-поддержка для бизнеса на Юге России — главный хаб компании Сервакисервисы **Стек:** Статический HTML + Nginx (Beget shared hosting) diff --git a/SITE-BUILD.md b/SITE-BUILD.md new file mode 100644 index 0000000..058ecdf --- /dev/null +++ b/SITE-BUILD.md @@ -0,0 +1,67 @@ +# servaki.online — расширение лендинга + +Подготовлено и опубликовано 28 сентября 2026 на существующем Beget-хостинге `infogypa`. Сертификат Let's Encrypt заказан повторно и ожидает выпуска со стороны Beget. + +## Состав + +- Главная с 12 тематическими иллюстрациями под описаниями карточек и ссылками на профильные страницы. +- 12 статей-лендингов: 3 подхода, 6 услуг, 3 отрасли. 3525 слов основного содержания с FAQ. +- Каталоги `/approach/`, `/services/`, `/industries/`. +- Автор, дата, хлебные крошки, оглавления, связанные материалы, контакты. +- Уникальные title/description/canonical, Open Graph, Article/Service/FAQPage/BreadcrumbList, sitemap, robots. +- 12 тематических AI-иллюстраций для карточек главной и 12 тёплых фотореалистичных AI-изображений для внутренних страниц, с WebP-версиями 480/960/1536 px. У каждой статьи своё изображение и подпись о том, что оно создано ИИ без данных и фотографий клиентов. +- На страницах услуг — конкретные пояснения о согласовании доступа, ограничении видимости данных и бережной работе в действующем офисе или на объекте. Это описание подхода, а не обещание абсолютной безопасности. +- В тексте каждой из 12 статей — тематическая схема работы оборудования: индикаторы приборов и движение сигнала между ними. Схемы состоят из компактных SVG и CSS, не загружают GIF или видео. При настройке «уменьшить движение» индикаторы и путь остаются статичными. +- Описания под всеми 12 карточками главной переписаны: задача клиента, способ работы и результат; условия отклика указаны без обещания устранить любой сбой за 15 минут. У трёх каталогов и главной — отдельные поисковые описания. + +## Просмотр и сборка + +Текущий предпросмотр: http://127.0.0.1:5186/ . Использовать HTTP-сервер, не открывать index.html как file://: пути начинаются от корня сайта. + +Python 3.12+, зависимости в `requirements.txt`: Pillow для экспорта изображений, Paramiko для SFTP и cryptography для проверки сертификата. HTML генерируется стандартной библиотекой, Node и npm для этой сборки не нужны. + +``` +python -m pip install -r requirements.txt +python prepare_images.py +python prepare_photos.py +python build.py +python -m http.server 5186 --bind 127.0.0.1 --directory public +python verify.py +``` + +`content/pages.json` — тексты; `content/card-copy.json` — тексты карточек; `content/flow-scenes.json` — подписи и оборудование для анимированных схем; `content/image-prompts.json` и `content/photo-prompts.json` — промпты встроенного image_gen; `content/photo-alt.json` и `content/privacy-notes.json` — описания фото и редакционные пояснения; `artwork/` — оригиналы; `assets/` — CSS и WebP; `public/` — готовый сайт; `reference/original.html` — сохранённая исходная главная. + +## Фактическая основа + +Внутренние заметки используются только как подтверждение исторического инженерного опыта, без имён заказчиков, адресов, паролей и топологии: + +- fleet-vault/daily/2026-09-28-antosha-fleet-traffic-audit.md — диагностика туннелей по реальному трафику, сохранение настроек и проверка зависимостей. +- fleet-vault/memory/vesovaya-bitarov-most-remzona-2026-07-21.md — обследование связи весовой и ремонтной зоны, сопоставление доступности и uptime. +- fleet-vault/memory/mpo-inform-site-2026-07-01.md — работа со схемами камер и СКУД, необходимость отделять условные модели от точных монтажных данных. +- fleet-vault/projects/controllers-energy-digest-bot.md — Wiren Board/MQTT, показатели энергии, существующие сценарии и отдельно запланированные функции. +- fleet-vault/inventory/fleet-hosts.md — опыт серверной виртуализации и контейнеров. Наличие каждого старого сервиса сейчас не заявляется. + +Для HoReCa описана применимость сетевого и серверного опыта, не придуманы гостиничные кейсы, отзывы, гарантированные результаты или количество клиентов. 28 сентября 2026 по прямому указанию владельца цены трёх тарифов увеличены втрое: «Старт» — 9 000 ₽/мес., «Бизнес» — 21 000 ₽/мес., «Премиум» — 45 000 ₽/мес. Состав тарифов не менялся; отклик и срок устранения разделены. + +## SEO и ИИ-поиск + +Основа: доступный HTML-текст, нормальная внутренняя навигация, точные описания, вопросы и ответы, изображения и соответствующая видимому тексту разметка. Специальный файл llms.txt не нужен для показа в AI-функциях Google и не представлен как условие индексации или гарантия цитирования. + +Источники: https://developers.google.com/search/docs/appearance/ai-features и https://developers.google.com/search/docs/appearance/snippet . Попадание в индекс и ответы ИИ не гарантируется. FAQ-разметка описывает видимый FAQ, не обещает расширенный сниппет. + +## Проверки + +`verification.json`: 16 HTML-страниц локально HTTP 200, побайтная сверка, уникальные title и description, H1, JSON-LD, все внутренние ссылки/якоря и изображения, 16 адресов sitemap. На 12 статьях проверены схемы с тремя приборами и двумя путями сигнала; мобильные фото весят не более 38,7 КБ, общий CSS — 12,6 КБ. Отрицательный контроль: намеренно сломанная ссылка на изображение обнаруживается. + +`live-verification.json`: 21 публичный URL проверен по HTTPS с временным отключением проверки старого сертификата; все отдали 200 и байт-в-байт совпали со сборкой. Публичный сертификат на момент проверки ещё истёк (17.09.2026); заявка на бесплатный перевыпуск принята панелью Beget 28.09.2026. До выпуска сертификата браузеры по-прежнему могут показывать предупреждение. + +Браузер: главная и статья на обычном экране и 390×844; горизонтального переполнения нет, responsive images загружаются, оглавление и FAQ работают. Анимированная схема сети визуально проверена на 1280 px и в мобильном окне 390 px: устройства выстраиваются вертикально, путь сигнала сохраняется. Исходная форма на главной остаётся демонстрационной; все новые CTA ведут прямо в Telegram или на телефон, сообщения автоматически не отправлялись. + +## Публикация и откат + +- Боевой каталог подтверждён по SFTP: `/home/i/infogypa/servaki.online/public_html`. Перед публикацией его `index.html` побайтно совпал с `reference/original.html`; новые каталоги `approach`, `services`, `industries`, `assets/editorial` отсутствовали. Семь прежних изображений в `img/` совпали с исходными и не перезаписывались. +- `deploy_beget.py audit` сохранил изменяемые исходные файлы в `release-backups/2026-09-28-before-landing/`; дубликат главной размещён на Beget вне публичного каталога: `/home/i/infogypa/servaki.online/backup-2026-09-28-before-landing/`. +- `deploy_beget.py deploy` загрузил и сверил SHA-256 всех 98 файлов. Сначала были загружены графика и внутренние страницы, главная заменена последней. Скрипт фиксирует SSH-отпечаток хостинга и не перезаписывает посторонние файлы. +- `deploy_beget.py reprice` меняет только главную: перед загрузкой проверяет, что шесть изменений касаются исключительно трёх видимых цен и трёх цен в JSON-LD. Предыдущая главная сохраняется локально в `release-backups/2026-09-28-before-tripled-prices/` и на Beget вне публичного каталога в `backup-2026-09-28-before-tripled-prices/`. +- `.htaccess`, DNS, почта и другие сайты не менялись. Исходный репозиторий сайта — `german/site-servaki-online` в Gitea; в `servaki-monitor` есть чужие незакоммиченные изменения, они не затрагивались. Публикация выполнялась напрямую на существующий Beget-хостинг, без автоматического деплоя из Gitea. +- Для отката вернуть из резервной копии прежний `index.html`; у нового релиза были добавлены `robots.txt`, `sitemap.xml` и каталоги `approach`, `services`, `industries`, `assets/editorial`. Удалять общие каталоги `img` и другие сайты нельзя. diff --git a/assets/editorial.css b/assets/editorial.css new file mode 100644 index 0000000..200beb5 --- /dev/null +++ b/assets/editorial.css @@ -0,0 +1,12 @@ +:root{--ink:#eef2ff;--muted:#a8b9db;--cyan:#22d3ee;--blue:#3b82f6;--violet:#8b5cf6;--line:rgba(255,255,255,.12);--bg:#05070f;--panel:#0c1326;--max:1200px}*{box-sizing:border-box}html{scroll-behavior:smooth;scroll-padding-top:100px}body{margin:0;background:radial-gradient(ellipse at 10% 0,rgba(59,130,246,.13),transparent 45%),radial-gradient(ellipse at 100% 25%,rgba(139,92,246,.08),transparent 40%),var(--bg);color:var(--ink);font:16px/1.75 Inter,system-ui,sans-serif}a{color:inherit;text-decoration:none}a:focus-visible,summary:focus-visible{outline:3px solid var(--cyan);outline-offset:6px}img{max-width:100%;display:block}h1,h2,h3,.brand-name{font-family:'Space Grotesk',Inter,system-ui,sans-serif;line-height:1.15;letter-spacing:-.035em}h1{font-size:clamp(2.15rem,4.3vw,4.25rem);margin:20px 0 24px;max-width:960px}h2{font-size:clamp(1.55rem,2.3vw,2.25rem);margin:0 0 24px}h3{font-size:1.25rem}p{color:var(--muted);margin:0 0 20px}ul{padding-left:22px}.wrap{width:min(calc(100% - 48px),var(--max));margin:auto}.skip{position:fixed;top:-100px;left:24px;padding:12px;background:var(--cyan);color:#05070f;z-index:100}.skip:focus{top:8px}.site-nav{position:sticky;top:0;z-index:20;background:rgba(5,7,15,.94);border-bottom:1px solid var(--line);backdrop-filter:blur(15px)}.nav-row{display:flex;align-items:center;justify-content:space-between;gap:20px;min-height:82px}.brand{display:flex;align-items:center;gap:12px}.brand-orbit{display:grid;place-items:center;width:38px;height:38px;border-radius:12px;background:linear-gradient(135deg,var(--cyan),var(--blue),var(--violet));color:#071225;font-weight:800}.brand-name{font-size:20px;letter-spacing:-.03em}.brand small{display:block;font-size:10px;font-family:Inter,system-ui,sans-serif;letter-spacing:.03em;color:var(--muted);margin-top:5px}.nav-links{display:flex;align-items:center;gap:28px;font-size:14px}.nav-links>a:hover,.crumbs a:hover,.toc a:hover{color:var(--cyan)}.button{display:inline-flex;align-items:center;justify-content:center;padding:13px 22px;border-radius:12px;background:linear-gradient(120deg,var(--cyan),var(--blue));color:#051222;font-weight:700;font-size:14px;line-height:1.4;transition:transform .2s}.button:hover{transform:translateY(-2px)}.button.secondary{border:1px solid var(--line);background:rgba(255,255,255,.03);color:var(--ink)}.crumbs{display:flex;flex-wrap:wrap;gap:10px;list-style:none;padding:0;margin:0;font-size:12px;color:var(--muted)}.crumbs li+li:before{content:' / ';margin-right:10px;color:#53668d}.intro{padding:38px 0 50px}.eyebrow{display:inline-block;margin-top:38px;color:var(--cyan);font-size:11px;letter-spacing:.16em;text-transform:uppercase}.intro .lead{font-size:clamp(17px,1.6vw,21px);max-width:840px;line-height:1.7}.actions{display:flex;gap:12px;flex-wrap:wrap;margin-top:26px}.meta{font-size:12px;color:#92a5cd;margin-top:26px}.hero-figure{margin:0;position:relative;border:1px solid var(--line);border-radius:24px;overflow:hidden;background:#080c1a}.hero-figure img{width:100%;height:auto;aspect-ratio:3/2;object-fit:cover}.hero-figure figcaption{position:absolute;bottom:0;left:0;right:0;padding:35px 30px 22px;background:linear-gradient(transparent,rgba(5,7,15,.94));font-size:12px;color:#c1cde5}.strengths{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:var(--line);border:1px solid var(--line);border-radius:18px;overflow:hidden;margin:26px 0 72px}.strength{padding:26px;background:#080d1b;font-size:15px;line-height:1.6}.strength span{display:block;color:var(--cyan);font-size:11px;letter-spacing:.1em;margin-bottom:12px}.reading-grid{display:grid;grid-template-columns:250px minmax(0,1fr);gap:80px;align-items:start}.toc{position:sticky;top:112px;font-size:13px;line-height:1.6;border-top:1px solid var(--line);padding-top:20px}.toc strong{display:block;color:#fff;margin-bottom:16px}.toc ol{padding:0;list-style:none;display:grid;gap:14px;margin:0}.toc a{color:var(--muted)}.article-body{max-width:800px}.article-body section{margin:0 0 48px}.article-body section p{font-size:17px;line-height:1.9}.article-body section+section{border-top:1px solid var(--line);padding-top:38px}.faq{margin-top:60px}.faq details{border-bottom:1px solid var(--line);padding:20px 0}.faq summary{cursor:pointer;list-style:none;font-weight:600;padding-right:35px;position:relative;line-height:1.6}.faq summary::-webkit-details-marker{display:none}.faq summary:after{content:'+';position:absolute;right:4px;top:-2px;font-size:24px;color:var(--cyan)}.faq details[open] summary:after{content:'−'}.faq details p{padding-top:18px;margin-bottom:0;font-size:15px}.consult{border:1px solid rgba(34,211,238,.24);border-radius:24px;padding:clamp(24px,5vw,60px);margin:75px 0;background:radial-gradient(ellipse at right,rgba(59,130,246,.13),transparent 75%)}.consult p{max-width:760px}.consult .eyebrow{margin-top:0;margin-bottom:16px}.related{padding-bottom:80px}.related-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:24px}.related-card{border:1px solid var(--line);border-radius:18px;overflow:hidden;background:linear-gradient(140deg,rgba(255,255,255,.05),rgba(255,255,255,.01));transition:border-color .2s}.related-card:hover{border-color:var(--cyan)}.related-card img{aspect-ratio:3/2;object-fit:cover;width:100%;height:auto}.related-card .copy{padding:24px}.related-card h3{margin:0 0 12px}.related-card p{font-size:13px;margin-bottom:15px;line-height:1.7}.link-arrow{color:var(--cyan);font-size:13px;font-weight:600}.site-footer{border-top:1px solid var(--line);padding:35px 0 45px;font-size:13px;color:var(--muted)}.footer-row{display:flex;justify-content:space-between;gap:25px;flex-wrap:wrap}.site-footer a{color:var(--ink)}.footer-links{display:flex;gap:20px;flex-wrap:wrap}.hub-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:24px;margin:40px 0 70px}.hub-intro{max-width:820px}.card-landing{display:block;margin-top:22px;border-radius:12px;overflow:hidden;border:1px solid rgba(255,255,255,.12);position:relative;z-index:2}.card-landing img{display:block;width:100%;height:auto;aspect-ratio:3/2;object-fit:cover;transition:transform .3s}.card-landing span{display:block;padding:14px 15px;background:#0c1326;color:#67e8f9;font-size:13px;font-weight:600;line-height:1.4}.card-landing:hover img{transform:scale(1.025)}.card-landing:focus-visible{outline:3px solid #22d3ee;outline-offset:4px}.ind .card-landing{margin:0 24px 24px}.ind .top{display:none}.ind .body{padding-top:26px}.card p{flex-grow:0}.card-landing{margin-top:auto}.card h3+p{margin-bottom:22px}.card .stack{margin-bottom:20px}.content-note{font-size:12px;color:var(--muted)} +@media(max-width:900px){.reading-grid{grid-template-columns:1fr;gap:32px}.toc{position:static}.toc ol{grid-template-columns:1fr 1fr;display:grid;gap:12px 24px}.article-body{max-width:none}.nav-links{gap:18px}.nav-links .desktop-link{display:none}.related-grid,.hub-grid{gap:16px}.related-card .copy{padding:18px}.strengths{margin-bottom:45px}} +@media(max-width:600px){.wrap{width:calc(100% - 32px)}.nav-row{min-height:70px;gap:10px}.brand-name{font-size:16px}.brand small{font-size:8px}.brand-orbit{width:30px;height:30px;border-radius:9px}.nav-links{gap:8px}.nav-links .button{font-size:12px;padding:10px 12px}.intro{padding:24px 0 32px}.eyebrow{margin-top:30px;font-size:10px}.hero-figure{border-radius:16px}.hero-figure figcaption{position:static;padding:13px 16px;background:#080c1a;font-size:10px}.strengths{grid-template-columns:1fr}.strength{padding:20px}.strength span{margin-bottom:6px}.toc ol{grid-template-columns:1fr}.related-grid,.hub-grid{grid-template-columns:1fr}.article-body section p{font-size:16px}.article-body section{margin-bottom:32px}.consult{margin:45px 0}.actions .button{flex:1 1 160px}.meta{line-height:1.8}.related{padding-bottom:45px}.footer-links{gap:14px}.card-landing span{font-size:12px}} +@media(max-width:600px){.actions .button{flex-basis:100%;white-space:nowrap}} +.privacy-note{position:relative;margin:28px 0 22px;padding:22px 27px;border:1px solid rgba(249,185,108,.25);border-left:3px solid #e6ab70;border-radius:13px;background:linear-gradient(110deg,rgba(149,91,54,.17),rgba(255,255,255,.025));max-width:820px}.privacy-note span{display:block;color:#f2c598;font-size:11px;font-weight:700;letter-spacing:.13em;margin-bottom:8px}.privacy-note p{margin:0;color:#d5c8c0;font-size:15px;line-height:1.7}.hero-figure img{filter:saturate(.93) contrast(1.02)} +.article-top{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(350px,.85fr);gap:50px;align-items:start;padding-top:12px}.article-top .intro{padding:26px 0 18px}.article-top h1{font-size:clamp(2.15rem,3.35vw,3.55rem)}.article-top .hero-figure{margin:30px 0 0}.article-top .hero-figure img{aspect-ratio:3/2;object-fit:cover}.article-top .meta{margin-bottom:0} +@media(max-width:1000px){.article-top{grid-template-columns:1fr;gap:0}.article-top .intro{padding-bottom:16px}.article-top .hero-figure{margin:0}.article-top h1{font-size:clamp(2.15rem,4.3vw,4.25rem)}} +@media(max-width:600px){.privacy-note{padding:18px 20px;margin:20px 0}.privacy-note p{font-size:14px}} +@media(prefers-reduced-motion:reduce){html{scroll-behavior:auto}*,*:before,*:after{animation:none!important;transition:none!important}} +.system-flow{margin:-20px 0 55px;padding:26px;border:1px solid rgba(34,211,238,.21);border-radius:20px;background:radial-gradient(circle at 80% 0,rgba(34,211,238,.1),transparent 50%),#0a1121}.system-flow figcaption{display:grid;gap:4px;margin-bottom:25px}.flow-kicker{color:#67e8f9;font-size:10px;font-weight:700;letter-spacing:.15em}.system-flow figcaption strong{font:600 clamp(19px,2vw,23px)/1.35 'Space Grotesk',Inter,sans-serif;color:#f5f8ff}.system-flow figcaption small{font-size:12px;line-height:1.55;color:#a8b9db}.flow-track{display:flex;align-items:stretch;gap:0}.flow-device{position:relative;display:flex;flex:1;min-width:0;min-height:130px;flex-direction:column;justify-content:space-between;padding:16px;border:1px solid rgba(255,255,255,.13);border-radius:14px;background:rgba(255,255,255,.035)}.flow-device-top{display:flex;align-items:flex-start;justify-content:space-between}.flow-icon{display:grid;place-items:center;width:42px;height:42px;border-radius:12px;background:rgba(34,211,238,.13);color:#67e8f9}.flow-icon svg{width:25px;height:25px}.flow-led{width:9px;height:9px;border-radius:50%;background:#22c55e;box-shadow:0 0 0 3px rgba(34,197,94,.12);animation:flow-led-pulse 3.2s ease-in-out infinite}.flow-device:nth-child(3) .flow-led{animation-delay:.6s}.flow-device:nth-child(5) .flow-led{animation-delay:1.2s}.flow-device strong{font-size:13px;line-height:1.35;color:#eef2ff}.flow-device-index{position:absolute;right:14px;bottom:13px;color:#637799;font-size:10px}.flow-link{position:relative;flex:0 0 50px;align-self:center;height:2px;background:linear-gradient(90deg,rgba(34,211,238,.32),#67e8f9,rgba(34,211,238,.32))}.flow-link:before,.flow-link:after{content:'';position:absolute;right:-1px;top:-4px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:7px solid #67e8f9}.flow-link span{position:absolute;top:-3px;left:0;width:8px;height:8px;border-radius:50%;background:#fff;box-shadow:0 0 12px #67e8f9;animation:flow-packet 3.2s linear infinite}.flow-link:nth-child(4) span{animation-delay:1.6s}@keyframes flow-packet{0%{left:0;opacity:0}12%,85%{opacity:1}100%{left:calc(100% - 8px);opacity:0}}@keyframes flow-led-pulse{0%,100%{opacity:.4;box-shadow:0 0 0 2px rgba(34,197,94,.1)}45%{opacity:1;box-shadow:0 0 0 5px rgba(34,197,94,.17)}} +@media(max-width:600px){.system-flow{padding:20px;margin:0 0 42px}.flow-track{flex-direction:column}.flow-device{width:100%;min-height:84px;padding:12px 15px;flex-direction:row;align-items:center;gap:12px}.flow-icon{width:38px;height:38px}.flow-icon svg{width:23px;height:23px}.flow-device strong{flex:1}.flow-device-index{position:static}.flow-device-top{align-items:center}.flow-link{align-self:center;flex:0 0 28px;width:2px;height:28px;background:linear-gradient(180deg,rgba(34,211,238,.32),#67e8f9,rgba(34,211,238,.32))}.flow-link:before,.flow-link:after{right:-4px;top:auto;bottom:-1px;border-top:7px solid #67e8f9;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:0}.flow-link span{top:0;left:-3px;animation-name:flow-packet-vertical}@keyframes flow-packet-vertical{0%{top:0;opacity:0}12%,85%{opacity:1}100%{top:calc(100% - 8px);opacity:0}}} +@media(prefers-reduced-motion:reduce){.flow-link span{animation:none;opacity:1;left:50%;transform:translateX(-50%)}.flow-led{animation:none;opacity:1;box-shadow:0 0 0 3px rgba(34,197,94,.12)}} diff --git a/build.py b/build.py new file mode 100644 index 0000000..bc661a5 --- /dev/null +++ b/build.py @@ -0,0 +1,158 @@ +"""Static HTML build; no JavaScript required for article content or navigation.""" +from pathlib import Path +import json, html, re, shutil, math +from xml.etree import ElementTree as ET + +ROOT=Path(__file__).resolve().parent +OUT=ROOT/'public' +BASE='https://servaki.online' +DATE='2026-09-28' +PAGES=json.loads((ROOT/'content/pages.json').read_text(encoding='utf-8')) +BY_ID={p['slug']:p for p in PAGES} +PRIVACY=json.loads((ROOT/'content/privacy-notes.json').read_text(encoding='utf-8')) +PHOTO_ALT=json.loads((ROOT/'content/photo-alt.json').read_text(encoding='utf-8')) +FLOWS=json.loads((ROOT/'content/flow-scenes.json').read_text(encoding='utf-8')) +CARD_COPY=json.loads((ROOT/'content/card-copy.json').read_text(encoding='utf-8')) +GROUPS={'Подход':('approach','Как мы работаем','Поддержка с понятной ответственностью'),'Услуги':('services','IT-услуги для бизнеса','От рабочего места до инфраструктуры'),'Отрасли':('industries','IT-решения для отраслей','Учитываем, как работает ваш бизнес')} +HUB_DESCRIPTIONS={ + 'Подход':'Как servaki.online принимает обращения, поддерживает удалённые объекты и выстраивает абонентское IT-обслуживание бизнеса на КМВ и Юге России.', + 'Услуги':'Удалённая помощь, сети и VPN, мониторинг, видеонаблюдение, серверы и автоматизация для бизнеса на КМВ и Юге России. Состав работ и порядок поддержки.', + 'Отрасли':'IT-поддержка гостиниц и кафе, сельского хозяйства и строительных объектов: связь, рабочие системы, камеры и удалённый контроль на Юге России.' +} +ICON_PATHS={ + 'ticket':'', + 'laptop':'', + 'check':'', + 'sensor':'', + 'router':'', + 'settings':'', + 'shield':'', + 'user':'', + 'server':'', + 'bell':'', + 'camera':'', + 'database':'', + 'phone':'' +} +def esc(t): return html.escape(str(t),quote=True) +def write(rel,text): + p=OUT/rel;p.parent.mkdir(parents=True,exist_ok=True);p.write_text(text,encoding='utf-8') +def url(p):return '/'+p['path']+'/' +def image(p,lazy=True,small=False): + slug=p['slug']; sizes='(max-width: 600px) 100vw, 380px' if small else '(max-width: 1248px) 100vw, 1200px' + return f'{esc(p[' +def photo_image(p): + slug=p['slug'] + return image(p,lazy=False).replace(f'/{slug}-',f'/photo-{slug}-').replace(esc(p['alt']),esc(PHOTO_ALT[slug])).replace('(max-width: 1248px) 100vw, 1200px','(max-width: 600px) calc(100vw - 32px), (max-width: 1000px) calc(100vw - 48px), 520px') +def head(title,description,canonical,img,graph): + data=json.dumps({'@context':'https://schema.org','@graph':graph},ensure_ascii=False).replace('<','\\u003c') + return f'''{esc(title)} | servaki.online''' +NAV='''''' +FOOT='''''' +ORG={'@type':'Organization','@id':BASE+'/#organization','name':'servaki.online','url':BASE+'/','telephone':'+79620151414','sameAs':['https://t.me/servaki_online'],'areaServed':['Юг России','Ставропольский край','Кавказские Минеральные Воды']} +def crumbs(items): + return '' +def related_card(p): + return f'{image(p,small=True)}

{esc(p["card"])}

{esc(p["label"])}

Подробнее
' +def flow_diagram(p): + scene=FLOWS[p['slug']] + devices=[] + for i,(icon,label) in enumerate(scene['nodes']): + if i:devices.append('') + glyph=f'' + devices.append(f'
{glyph}
{esc(label)}0{i+1}
') + return f'
ПРИНЦИП РАБОТЫ{esc(scene["title"])}Условная схема движения сигнала, без реальных данных и настроек клиентов.
{"".join(devices)}
' +def article(p): + canonical=BASE+url(p);img=BASE+'/assets/editorial/'+p['slug']+'-1536.webp';g=GROUPS[p['group']] + article_id=canonical+'#article' + graph=[ORG,{'@type':'WebPage','@id':canonical,'url':canonical,'name':p['title'],'description':p['description'],'inLanguage':'ru-RU','isPartOf':{'@id':BASE+'/#website'},'mainEntity':{'@id':article_id}}, {'@type':'Article','@id':article_id,'headline':p['title'],'description':p['description'],'image':[img],'author':{'@id':BASE+'/#organization'},'publisher':{'@id':BASE+'/#organization'},'datePublished':DATE,'dateModified':DATE,'inLanguage':'ru-RU','mainEntityOfPage':{'@id':canonical}}, {'@type':'BreadcrumbList','itemListElement':[{'@type':'ListItem','position':i+1,'name':n,'item':BASE+u} for i,(n,u) in enumerate([('Главная','/'),(g[1],'/'+g[0]+'/'),(p['card'],url(p))])]}, {'@type':'FAQPage','@id':canonical+'#faq','mainEntity':[{'@type':'Question','name':q,'acceptedAnswer':{'@type':'Answer','text':a}} for q,a in p['faq']]}] + if p['group'] in ['Услуги','Отрасли']: + graph.append({'@type':'Service','@id':canonical+'#service','name':p['title'],'description':p['lead'],'provider':{'@id':BASE+'/#organization'},'areaServed':['Ставропольский край','Кавказские Минеральные Воды','Юг России'],'url':canonical}) + toc=''.join(f'
  • {esc(h)}
  • '+('
  • Схема работы оборудования
  • ' if i==1 else '') for i,(h,_) in enumerate(p['sections'],1)) + sections=''.join(f'

    {esc(h)}

    {esc(t)}

    '+ (flow_diagram(p) if i==1 else '') for i,(h,t) in enumerate(p['sections'],1)) + strengths=''.join(f'
    0{i} / НАШ ПОДХОД{esc(t)}
    ' for i,t in enumerate(p['strengths'],1)) + faq=''.join(f'
    {esc(q)}

    {esc(a)}

    ' for q,a in p['faq']) + minutes=max(3,math.ceil(len(' '.join(t for _,t in p['sections']).split())/170)) + return head(p['title'],p['description'],canonical,img,graph)+NAV+f'''
    {crumbs([('Главная','/'),(g[1],'/'+g[0]+'/'),(p['card'],None)])}{esc(p['group'])} / {esc(p['label'])}

    {esc(p['title'])}

    {esc(p['lead'])}

    Команда servaki.online · · {minutes} мин чтения

    {image(p,lazy=False)}
    {esc(p['label'])} · Тематическая AI-иллюстрация
    {strengths}
    {sections}

    Вопросы и ответы

    {faq}
    Следующий шаг

    Расскажите, что должно работать

    Укажите ваш объект, существующее оборудование и задачу. Разберём исходные условия, уточним необходимые доступы и предложим порядок работ. Стоимость и сроки согласуем до начала.

    '''+FOOT +def home(original): + result=original + for p in PAGES: + pattern=r']*>\s*(?:(?!).)*

    '+re.escape(p['card'])+r'

    (?:(?!).)*' + match=re.search(pattern,result,re.S) + if not match:raise RuntimeError('Missing homepage card: '+p['card']) + block=match.group() + heading_end=block.find('')+len('') + before,after=block[:heading_end],block[heading_end:] + after,count=re.subn(r'(]*>).*?(

    )',lambda m:m.group(1)+esc(CARD_COPY[p['slug']])+m.group(2),after,count=1,flags=re.S) + if count!=1:raise RuntimeError('Missing homepage description: '+p['card']) + block=before+after + # Industry illustrations move below their descriptions, consistent with all cards. + block=re.sub(r'
    .*?
    ','',block,flags=re.S) + link=f'{image(p,small=True)}Подробнее: {esc(p["card"])} ' + block=block.replace('',link+'') + result=result[:match.start()]+block+result[match.end():] + # Scope supplementary styles to homepage components; article reset must not overwrite live home. + css='''''' + css=css.replace('.card-landing span{display:block;', '.card-landing span{display:block;min-height:67px;') + result=result.replace('',css+'') + result=re.sub(r'.*?','IT-поддержка бизнеса на КМВ и Юге России — servaki.online',result,count=1,flags=re.S) + result=re.sub(r'(Услуги','
  • Все услуги
  • ') + result=result.replace('
  • Отрасли
  • ','
  • Решения для отраслей
  • ') + result=result.replace('
  • О нас
  • ','
  • Наш подход
  • ') + # Connect process/pricing context to detailed pages. + result=result.replace('Простой и прозрачный процесс — от вашего сообщения до постоянного мониторинга.','Простой и прозрачный процесс — от вашего сообщения до постоянного мониторинга. Как устроена поддержка →') + result=result.replace('

    Прозрачные подписки

    ','

    Прозрачные подписки

    Что входит в обслуживание и как выбрать тариф →

    ') + for old in (3000, 7000, 15000): + structured=f'"price": "{old}"' + visible=f'{old}' + if result.count(structured)!=1 or result.count(visible)!=1: + raise RuntimeError(f'Expected one visible and one structured price for {old}') + new=old*3 + result=result.replace(structured,f'"price": "{new}"') + result=result.replace(visible,f'{new:,}'.replace(',', ' ')) + return result +def build(): + OUT.mkdir(exist_ok=True) + dest=OUT/'assets/editorial';dest.mkdir(parents=True,exist_ok=True) + shutil.copy2(ROOT/'assets/editorial.css',dest/'editorial.css') + for p in PAGES: + for width in [480,960,1536]: + img=ROOT/'assets'/f'{p["slug"]}-{width}.webp' + if img.exists():shutil.copy2(img,dest/img.name) + photo=ROOT/'assets'/f'photo-{p["slug"]}-{width}.webp' + if not photo.exists():raise RuntimeError('Missing warm photograph: '+str(photo)) + shutil.copy2(photo,dest/photo.name) + page=article(p) + page=page.replace(image(p,lazy=False),photo_image(p),1) + page=page.replace(f'/assets/editorial/{p["slug"]}-1536.webp',f'/assets/editorial/photo-{p["slug"]}-1536.webp') + page=page.replace(' · Тематическая AI-иллюстрация',' · Изображение создано ИИ, без данных и фотографий клиентов',1) + page=page.replace('
    ','
    ',1) + page=page.replace('
    ','
    ',1) + note=f'' + page=page.replace('
    ','
    '+note+'
    ',1) + write(p['path']+'/index.html',page) + original=(ROOT/'reference/original.html').read_text(encoding='utf-8') + write('index.html',home(original)) + for group,(slug,title,label) in GROUPS.items(): + desc=HUB_DESCRIPTIONS[group] + write(slug+'/index.html',head(title,desc,BASE+'/'+slug+'/',BASE+'/assets/editorial/remote-sites-1536.webp',[ORG,{'@type':'CollectionPage','name':title,'url':BASE+'/'+slug+'/','inLanguage':'ru-RU'}])+NAV+f'
    {crumbs([("Главная","/"),(title,None)])}{label}

    {title}

    {esc(desc)}

    '+''.join(related_card(p) for p in PAGES if p['group']==group)+'
    '+FOOT) + # Preserve existing URLs when a previous sitemap is available. + locations={BASE+'/',*[BASE+url(p) for p in PAGES],*[BASE+'/'+v[0]+'/' for v in GROUPS.values()]} + previous=ROOT/'reference/sitemap.xml' + if previous.exists(): + try:locations.update(e.text for e in ET.fromstring(previous.read_bytes()).iter() if e.tag.endswith('}loc') and e.text.startswith(BASE+'/')) + except ET.ParseError:pass + write('sitemap.xml','\n'+''.join(f'{esc(u)}' for u in sorted(locations))+'') + robots=ROOT/'reference/robots.txt' + r=robots.read_text(encoding='utf-8') if robots.exists() else 'User-agent: *\nAllow: /\n' + if ' str: + return hashlib.sha256(data).hexdigest() + + +class PinnedHost(paramiko.MissingHostKeyPolicy): + def missing_host_key(self, client, hostname, key): + actual = 'SHA256:' + base64.b64encode(hashlib.sha256(key.asbytes()).digest()).decode().rstrip('=') + if hostname != HOST or actual != HOST_FINGERPRINT: + raise paramiko.SSHException('Beget SSH host key changed; deployment stopped') + client.get_host_keys().add(hostname, key.get_name(), key) + + +def connect(): + access = ACCESS_DOC.read_text(encoding='utf-8') + match = re.search(r'\*\*Пароль:\*\*\s*`([^`]+)`', access) + if not match: + raise RuntimeError('FTP/SFTP credential entry not found') + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(PinnedHost()) + ssh.connect(HOST, username=USER, password=match.group(1), look_for_keys=False, + allow_agent=False, timeout=20, auth_timeout=20, banner_timeout=20) + sftp = ssh.open_sftp() + if sftp.normalize(WEBROOT) != WEBROOT: + raise RuntimeError('Unexpected Beget document root') + return ssh, sftp + + +def exists(sftp, path): + try: + return sftp.stat(path) + except FileNotFoundError: + return None + + +def remote_bytes(sftp, path): + with sftp.open(path, 'rb') as handle: + return handle.read() + + +def mkdirs(sftp, directory): + missing = [] + while not exists(sftp, directory): + missing.append(directory) + directory = posixpath.dirname(directory) + for item in reversed(missing): + sftp.mkdir(item) + + +def manifest(): + public = ROOT / 'public' + return {p.relative_to(public).as_posix(): digest(p.read_bytes()) + for p in public.rglob('*') if p.is_file()} + + +def audit(): + ssh, sftp = connect() + try: + before = {'host_fingerprint': HOST_FINGERPRINT, 'webroot': WEBROOT, + 'files': {}, 'top_entries': sorted(sftp.listdir(WEBROOT)), + 'release_files': manifest()} + live_index = remote_bytes(sftp, WEBROOT + '/index.html') + expected = (ROOT / 'reference' / 'original.html').read_bytes() + if live_index != expected: + raise RuntimeError('Live index differs from the reviewed source; merge before release') + for name in CORE: + path = WEBROOT + '/' + name + data = remote_bytes(sftp, path) if exists(sftp, path) else None + before['files'][name] = {'existed': data is not None, + 'sha256': digest(data) if data is not None else None} + if data is not None: + BACKUP.mkdir(parents=True, exist_ok=True) + target = BACKUP / name + if target.exists() and target.read_bytes() != data: + raise RuntimeError('Local backup differs from current Beget file: ' + name) + target.write_bytes(data) + for dirname in ('approach', 'services', 'industries'): + if exists(sftp, WEBROOT + '/' + dirname): + raise RuntimeError('Existing remote directory needs review: ' + dirname) + if exists(sftp, WEBROOT + '/assets/editorial'): + raise RuntimeError('Existing editorial assets need review') + BACKUP.mkdir(parents=True, exist_ok=True) + (BACKUP / 'before.json').write_text(json.dumps(before, ensure_ascii=False, indent=2), encoding='utf-8') + print('Audit passed: live homepage matches source; new editorial paths are clear.') + print('Backed up existing core files:', [k for k, v in before['files'].items() if v['existed']]) + print('Planned static files:', len(before['release_files'])) + finally: + sftp.close(); ssh.close() + + +def put_atomic(sftp, local, remote, replace=False): + mkdirs(sftp, posixpath.dirname(remote)) + temp = remote + '.codex-upload-' + uuid.uuid4().hex[:10] + sftp.put(str(local), temp, confirm=True) + if digest(remote_bytes(sftp, temp)) != digest(local.read_bytes()): + raise RuntimeError('Upload verification failed: ' + remote) + if replace: + try: + sftp.posix_rename(temp, remote) + except OSError: + shifted = REMOTE_BACKUP + '/' + posixpath.basename(remote) + '.shifted' + sftp.rename(remote, shifted) + try: + sftp.rename(temp, remote) + except Exception: + sftp.rename(shifted, remote) + raise + else: + sftp.rename(temp, remote) + + +def deploy(): + before = json.loads((BACKUP / 'before.json').read_text(encoding='utf-8')) + current = manifest() + if current != before['release_files']: + raise RuntimeError('Built site changed after audit; rerun audit') + ssh, sftp = connect() + try: + if digest(remote_bytes(sftp, WEBROOT + '/index.html')) != before['files']['index.html']['sha256']: + raise RuntimeError('Live homepage changed after audit; deployment stopped') + # Refuse to overwrite any unreviewed route or asset. + for rel, expected_hash in current.items(): + if rel in CORE: + continue + path = WEBROOT + '/' + rel + if exists(sftp, path) and digest(remote_bytes(sftp, path)) != expected_hash: + raise RuntimeError('Existing remote file differs: ' + rel) + mkdirs(sftp, REMOTE_BACKUP) + for name, state in before['files'].items(): + if state['existed']: + target = REMOTE_BACKUP + '/' + name + if not exists(sftp, target): + put_atomic(sftp, BACKUP / name, target) + elif digest(remote_bytes(sftp, target)) != state['sha256']: + raise RuntimeError('Remote backup mismatch: ' + name) + public = ROOT / 'public' + ordered = sorted((rel for rel in current if rel not in CORE), + key=lambda rel: (not rel.startswith('assets/'), rel)) + for rel in ordered: + target = WEBROOT + '/' + rel + if not exists(sftp, target): + put_atomic(sftp, public / rel, target) + for rel in ('robots.txt', 'sitemap.xml', 'index.html'): + target = WEBROOT + '/' + rel + if digest(remote_bytes(sftp, target)) == current[rel] if exists(sftp, target) else False: + continue + put_atomic(sftp, public / rel, target, replace=bool(exists(sftp, target))) + for rel, expected_hash in current.items(): + if digest(remote_bytes(sftp, WEBROOT + '/' + rel)) != expected_hash: + raise RuntimeError('Final remote checksum mismatch: ' + rel) + (BACKUP / 'deployed.json').write_text(json.dumps({'files':len(current),'sha256_index':current['index.html']},indent=2),encoding='utf-8') + print('Published and verified over SFTP:', len(current), 'files; backup outside public_html:', REMOTE_BACKUP) + finally: + sftp.close(); ssh.close() + + +def verify(): + current = manifest() + ssh, sftp = connect() + try: + failures = [rel for rel, expected in current.items() + if not exists(sftp, WEBROOT + '/' + rel) + or digest(remote_bytes(sftp, WEBROOT + '/' + rel)) != expected] + if failures: + raise RuntimeError('Remote file check failed: ' + ', '.join(failures[:5])) + print('Remote checksum verification passed:', len(current), 'files') + finally: + sftp.close(); ssh.close() + + +def reprice(): + """Replace only the published homepage after verifying the six price tokens.""" + before = json.loads((BACKUP / 'deployed.json').read_text(encoding='utf-8')) + new_index = (ROOT / 'public' / 'index.html').read_bytes() + new_hash = digest(new_index) + price_backup = ROOT / 'release-backups' / '2026-09-28-before-tripled-prices' + remote_price_backup = SITE + '/backup-2026-09-28-before-tripled-prices' + ssh, sftp = connect() + try: + live_index = remote_bytes(sftp, WEBROOT + '/index.html') + if digest(live_index) == new_hash: + print('Tariff update already published:', new_hash) + return + if digest(live_index) != before['sha256_index']: + raise RuntimeError('Live homepage changed since the previous release; reprice stopped') + expected = live_index.decode('utf-8') + for old in (3000, 7000, 15000): + structured = f'"price": "{old}"' + visible = f'{old}' + if expected.count(structured) != 1 or expected.count(visible) != 1: + raise RuntimeError(f'Unexpected original tariff markup: {old}') + new = old * 3 + expected = expected.replace(structured, f'"price": "{new}"') + expected = expected.replace(visible, f'{new:,}'.replace(',', ' ')) + if expected.encode('utf-8') != new_index: + raise RuntimeError('Built homepage has changes beyond the six tariff values') + price_backup.mkdir(parents=True, exist_ok=True) + local_copy = price_backup / 'index.html' + if local_copy.exists() and local_copy.read_bytes() != live_index: + raise RuntimeError('Existing local tariff backup differs from live homepage') + local_copy.write_bytes(live_index) + mkdirs(sftp, remote_price_backup) + remote_copy = remote_price_backup + '/index.html' + if exists(sftp, remote_copy): + if digest(remote_bytes(sftp, remote_copy)) != digest(live_index): + raise RuntimeError('Existing remote tariff backup differs from live homepage') + else: + put_atomic(sftp, local_copy, remote_copy) + put_atomic(sftp, ROOT / 'public' / 'index.html', WEBROOT + '/index.html', replace=True) + if digest(remote_bytes(sftp, WEBROOT + '/index.html')) != new_hash: + raise RuntimeError('Published tariff homepage checksum mismatch') + (price_backup / 'deployed.json').write_text(json.dumps({ + 'before_sha256': digest(live_index), 'after_sha256': new_hash, + 'remote_backup': remote_copy, 'prices_rub_month': [9000, 21000, 45000] + }, ensure_ascii=False, indent=2), encoding='utf-8') + print('Published tripled tariffs; homepage checksum:', new_hash) + finally: + sftp.close(); ssh.close() + + +if __name__ == '__main__': + {'audit': audit, 'deploy': deploy, 'verify': verify, 'reprice': reprice}[sys.argv[1]]() diff --git a/prepare_images.py b/prepare_images.py new file mode 100644 index 0000000..e5bbcce --- /dev/null +++ b/prepare_images.py @@ -0,0 +1,22 @@ +"""Preserve generated originals and export responsive WebP assets.""" +from pathlib import Path +from PIL import Image,ImageOps,ImageDraw +import json,shutil +ROOT=Path(__file__).resolve().parent +manifest=json.loads((ROOT/'content/image-manifest.json').read_text(encoding='utf-8')) +originals=ROOT/'artwork';originals.mkdir(exist_ok=True) +thumbs=[] +for item in manifest: + src=ROOT/item['path'];slug=item['slug'] + dest=originals/(slug+'.png') + if src.resolve()!=dest.resolve():shutil.copy2(src,dest) + im=Image.open(src).convert('RGB') + assert im.size==(1536,1024),(slug,im.size) + for width in (480,960,1536): + export=im.resize((width,round(width*2/3)),Image.Resampling.LANCZOS) + export.save(ROOT/'assets'/f'{slug}-{width}.webp',quality=82,method=6) + tile=Image.new('RGB',(384,286),'#05070f');tile.paste(im.resize((384,256)),(0,0));ImageDraw.Draw(tile).text((12,265),slug,fill='#67e8f9');thumbs.append(tile) +sheet=Image.new('RGB',(384*3,286*((len(thumbs)+2)//3)),'#05070f') +for i,t in enumerate(thumbs):sheet.paste(t,((i%3)*384,(i//3)*286)) +sheet.save(ROOT/'artwork/contact-sheet.jpg',quality=90) +print('Prepared',len(manifest),'illustrations; 3 sizes each') diff --git a/prepare_photos.py b/prepare_photos.py new file mode 100644 index 0000000..7214bbe --- /dev/null +++ b/prepare_photos.py @@ -0,0 +1,24 @@ +"""Preserve generated editorial photographs and export responsive WebP versions.""" +from pathlib import Path +from PIL import Image, ImageDraw, ImageOps +import json, shutil +ROOT=Path(__file__).resolve().parent +manifest=json.loads((ROOT/'content/photo-manifest.json').read_text(encoding='utf-8')) +originals=ROOT/'artwork/photos-selected'; originals.mkdir(parents=True,exist_ok=True) +tiles=[] +for item in manifest: + slug=item['slug']; source=ROOT/item['path'] + dest=originals/(slug+'.png') + if source.resolve()!=dest.resolve():shutil.copy2(source,dest) + picture=Image.open(source).convert('RGB') + picture=ImageOps.fit(picture,(1536,1024),method=Image.Resampling.LANCZOS,centering=(.5,.5)) + for width in (480,960,1536): + picture.resize((width,round(width*2/3)),Image.Resampling.LANCZOS).save(ROOT/'assets'/f'photo-{slug}-{width}.webp',quality=83,method=6) + tile=Image.new('RGB',(384,280),'#05070f') + tile.paste(picture.resize((384,256)),(0,0)) + ImageDraw.Draw(tile).text((12,263),slug,fill='#67e8f9') + tiles.append(tile) +sheet=Image.new('RGB',(1152,280*((len(tiles)+2)//3)),'#05070f') +for index,tile in enumerate(tiles):sheet.paste(tile,((index%3)*384,(index//3)*280)) +sheet.save(originals/'contact-sheet.jpg',quality=88) +print('Prepared',len(manifest),'warm editorial photographs in 3 responsive sizes') diff --git a/prepare_reference.py b/prepare_reference.py new file mode 100644 index 0000000..881bd14 --- /dev/null +++ b/prepare_reference.py @@ -0,0 +1,31 @@ +"""Download only public assets referenced by the existing homepage.""" +from pathlib import Path +from urllib.request import build_opener,ProxyHandler,HTTPSHandler,Request +from urllib.parse import urlsplit +import ssl,re,json +ROOT=Path(__file__).resolve().parent +BASE='https://servaki.online' +html=(ROOT/'reference/original.html').read_text(encoding='utf-8') +normal=build_opener(ProxyHandler({})) +try: + normal.open(BASE+'/',timeout=20).read(1) + print('TLS validation: OK') +except Exception as e: + print('TLS validation failed:',type(e).__name__) +# The original certificate was reported expired. This read-only fallback is +# restricted to public site resources; it never carries authentication data. +opener=build_opener(ProxyHandler({}),HTTPSHandler(context=ssl._create_unverified_context())) +assets=sorted(set(re.findall(r'(?:src|href)="(/[^"?#]+\.(?:png|jpg|jpeg|webp|svg|ico))"',html))) +manifest=[] +for rel in assets: + data=opener.open(BASE+rel,timeout=30).read() + dest=ROOT/'public'/rel.lstrip('/');dest.parent.mkdir(parents=True,exist_ok=True);dest.write_bytes(data) + manifest.append({'path':rel,'bytes':len(data)}) +for name in ['robots.txt','sitemap.xml']: + try: + response=opener.open(BASE+'/'+name,timeout=20);data=response.read() + if b'Как мы работаем | servaki.online
    Поддержка с понятной ответственностью

    Как мы работаем

    Как servaki.online принимает обращения, поддерживает удалённые объекты и выстраивает абонентское IT-обслуживание бизнеса на КМВ и Юге России.

    \ No newline at end of file diff --git a/public/approach/it-subscription/index.html b/public/approach/it-subscription/index.html new file mode 100644 index 0000000..2c1b4e9 --- /dev/null +++ b/public/approach/it-subscription/index.html @@ -0,0 +1 @@ +Абонентское IT-обслуживание бизнеса: состав и условия | servaki.online
    Подход / Понятный объём. Понятная ответственность.

    Абонентское IT-обслуживание бизнеса: состав и условия

    Подписка помогает планировать IT-расходы и закрепить ответственность за поддержку. Сначала описываем вашу инфраструктуру и нужный уровень обслуживания, затем согласуем состав работ.

    Команда servaki.online · · 3 мин чтения

    Владельцы семейного бизнеса и специалист обсуждают IT-поддержку в спокойной обстановке
    Понятный объём. Понятная ответственность. · Изображение создано ИИ, без данных и фотографий клиентов
    01 / НАШ ПОДХОДСостав обслуживания согласован до старта
    02 / НАШ ПОДХОДЕдиная картина сети, серверов и рабочих мест
    03 / НАШ ПОДХОДДополнительные работы оцениваются заранее

    Что стоит за ежемесячной оплатой

    Абонентское обслуживание — это согласованный набор задач: помощь сотрудникам, сопровождение оборудования, профилактика и отчётность. Фиксированная стоимость имеет смысл только вместе с понятными границами. До старта определяем количество рабочих мест, перечень серверов и объектов, часы поддержки и приоритет критичных сервисов.

    ПРИНЦИП РАБОТЫКак работает сопровождениеУсловная схема движения сигнала, без реальных данных и настроек клиентов.
    Рабочее место01
    Поддержка02
    Отчёт03

    Как выбрать подходящий уровень

    На главной опубликованы тарифы «Старт», «Бизнес» и «Премиум». Сопоставлять их лучше с задачами, а не только с числом компьютеров. Три рабочих места с критичной учётной системой могут требовать больше внимания, чем десять обычных офисных ПК. Несколько филиалов, камеры и резервные серверы учитываем при обсуждении состава обслуживания.

    Сильная сторона: видим связи между системами

    В нашей практике сети, серверы, видеонаблюдение и контроллеры работают совместно. Неисправность питания или одного маршрутизатора может проявиться сразу в нескольких сервисах. Поэтому при подключении описываем зависимости и общие точки отказа. Это даёт основу для профилактики и осмысленного плана развития.

    Что нужно согласовать отдельно

    Закупка оборудования, лицензии, монтаж, перенос сервисов и крупные изменения могут выходить за рамки регулярной поддержки. Такие задачи предварительно оцениваем. Порядок выездов, аварийные обращения вне рабочего времени и ограничения тарифа отражаем в договорённостях, чтобы у обеих сторон было одинаковое понимание услуги.

    Как начинается обслуживание

    Собираем перечень устройств, проверяем доступы и резервные копии, фиксируем известные проблемы. Приоритет отдаём рискам остановки бизнеса. Затем согласуем канал обращений и форму отчёта. По итогам сопровождения показываем выполненные работы и рекомендации, которые можно включить в следующий бюджет.

    Вопросы и ответы

    Какая цена будет у нашей компании?

    Ориентиры опубликованы на главной в разделе тарифов. Итог зависит от состава оборудования, числа объектов, часов поддержки и необходимых выездов.

    Можно ли начать с небольшого объёма?

    Да. Можно согласовать поддержку выбранных рабочих мест или сервисов и затем расширить состав обслуживания.

    Лицензии и оборудование включены?

    Это нужно проверить в предложении и договоре. Не следует считать закупки и сторонние подписки включёнными по умолчанию.

    Следующий шаг

    Расскажите, что должно работать

    Укажите ваш объект, существующее оборудование и задачу. Разберём исходные условия, уточним необходимые доступы и предложим порядок работ. Стоимость и сроки согласуем до начала.

    \ No newline at end of file diff --git a/public/approach/rapid-response/index.html b/public/approach/rapid-response/index.html new file mode 100644 index 0000000..5e3bc55 --- /dev/null +++ b/public/approach/rapid-response/index.html @@ -0,0 +1 @@ +Оперативная IT-поддержка бизнеса: как устроен отклик | servaki.online
    Подход / Помощь начинается с понимания задачи

    Оперативная IT-поддержка бизнеса: как устроен отклик

    Когда остановилась работа, важен понятный следующий шаг. Принимаем обращение, оцениваем влияние сбоя на бизнес и согласуем порядок восстановления. Время отклика закрепляем в выбранном тарифе.

    Команда servaki.online · · 3 мин чтения

    Специалист помогает владельцу небольшого семейного бизнеса в уютном офисе
    Помощь начинается с понимания задачи · Изображение создано ИИ, без данных и фотографий клиентов
    01 / НАШ ПОДХОДПриоритет по влиянию на работу бизнеса
    02 / НАШ ПОДХОДПроверка результата на реальной задаче сотрудника
    03 / НАШ ПОДХОДДокументирование изменений и дальнейших действий

    Что означает реакция от 15 минут

    Это время начала обработки обращения для соответствующего уровня обслуживания, а не обещание устранить любую неисправность за четверть часа. На главной представлены разные условия: у тарифа «Старт» отклик до двух часов, у «Бизнес» — 30 минут, у «Премиум» — 15 минут. Рабочее время поддержки, каналы связи и порядок аварийной эскалации фиксируем при подключении.

    ПРИНЦИП РАБОТЫОт обращения до проверки результатаУсловная схема движения сигнала, без реальных данных и настроек клиентов.
    Заявка01
    Диагностика02
    Проверка03

    Сначала восстанавливаем работу

    В заявке полезно указать, что перестало работать, когда начался сбой и скольких сотрудников он затронул. Остановка связи на объекте и настройка нового принтера требуют разного приоритета. Проверяем доступность, последние изменения и сообщения об ошибках. Затем предлагаем действие: восстановить соединение, вернуть рабочую конфигурацию, переключить резерв или подготовить выезд.

    Наш опыт: проверка всей цепочки

    В работе с распределёнными объектами мы сталкиваемся с ситуациями, когда роутер доступен, а нужный сервис не открывается. Поэтому проверяем не только индикатор подключения, но и прохождение данных, DNS и доступ к целевому приложению. Такой подход помогает отличить проблему провайдера от неисправности локальной сети или самого сервиса.

    Результат, который можно проверить

    После работ объясняем причину, перечисляем изменения и вместе с сотрудником проверяем исходную задачу: открылся ли документ, печатает ли принтер, доступна ли камера. Если требуется наблюдение, назначаем конкретную проверку. Для повторяющихся сбоев предлагаем план устранения причины, а не бесконечную серию перезапусков.

    Когда понадобится человек на месте

    Удалённое подключение не заменяет ремонт питания, кабеля или неисправного оборудования. В таких случаях готовим понятное задание для местного сотрудника или выезда: что проверить, какое устройство заменить, какие признаки зафиксировать. Это позволяет использовать время на объекте предметно.

    Вопросы и ответы

    За 15 минут будет устранён любой сбой?

    Нет. Отклик и срок решения — разные показатели. Время восстановления зависит от причины, доступа, оборудования и сторонних сервисов.

    Что отправить в первой заявке?

    Название объекта, краткое описание проблемы, время её появления и контакт сотрудника на месте. Пароли в обычное сообщение отправлять не нужно.

    Работает ли поддержка круглосуточно?

    Круглосуточный автоматический мониторинг и дежурство инженера — разные услуги. Часы поддержки и аварийный порядок согласуются отдельно.

    Следующий шаг

    Расскажите, что должно работать

    Укажите ваш объект, существующее оборудование и задачу. Разберём исходные условия, уточним необходимые доступы и предложим порядок работ. Стоимость и сроки согласуем до начала.

    \ No newline at end of file diff --git a/public/approach/remote-sites/index.html b/public/approach/remote-sites/index.html new file mode 100644 index 0000000..dc146d6 --- /dev/null +++ b/public/approach/remote-sites/index.html @@ -0,0 +1 @@ +IT-поддержка удалённых объектов: фермы, склады и базы | servaki.online
    Подход / Расстояние учтено в архитектуре

    IT-поддержка удалённых объектов: фермы, склады и базы

    Между офисом и объектом могут быть десятки километров и горный перевал. Строим поддержку вокруг доступности связи, удалённого управления и понятных действий для человека на месте.

    Команда servaki.online · · 3 мин чтения

    Инженер обсуждает связь удалённой фермы с её владельцами за столом
    Расстояние учтено в архитектуре · Изображение создано ИИ, без данных и фотографий клиентов
    01 / НАШ ПОДХОДОпыт диагностики радиомостов и удалённых площадок
    02 / НАШ ПОДХОДКонтроль реального обмена данными
    03 / НАШ ПОДХОДПлан действий при потере связи и питания

    Почему удалённой точке нужен свой подход

    За пределами города на работу IT влияют питание, радиомосты, качество мобильной связи и ограниченная возможность быстро заменить оборудование. Обычная схема офисной поддержки здесь недостаточна. Начинаем с карты зависимостей: откуда приходит интернет, что запитано от одного блока, где заканчивается ответственность провайдера и кто может помочь на площадке.

    ПРИНЦИП РАБОТЫСвязь с удалённой площадкойУсловная схема движения сигнала, без реальных данных и настроек клиентов.
    Объект01
    Канал связи02
    Инженер03

    Соединяем объект с офисом

    Настраиваем защищённый доступ к нужным ресурсам: учётной системе, камерам, контроллеру или рабочему месту. Разделяем служебные устройства и гостевые подключения. Для резервного канала проверяем реальное переключение и возвращение на основной, чтобы запасное соединение существовало не только в настройках.

    Практика: весовая и удалённая ремонтная зона

    В нашей практике есть диагностика связи между весовой и ремонтной зоной через радиомост. Для поиска причины наблюдали доступность устройств по обе стороны моста и время работы маршрутизатора. Так можно отделить перезагрузку оборудования от потери связи на конкретном участке и подготовить адресную проверку питания или радиолинии.

    Наблюдаем за полезной работой

    Сам факт подключения VPN не доказывает, что сотрудник откроет приложение. Проверяем обмен данными, доступность нужного сервиса и изменения после переключения канала. Для камер отдельно проверяем живое видео и архив; для контроллеров — свежесть измерений. Порог и получателей уведомлений выбираем под задачу объекта.

    Что получит ответственный за площадку

    Фиксируем состав оборудования, назначение каналов, порядок перезапуска и контакты для эскалации. Готовим короткую инструкцию для типовых ситуаций. Если восстановление требует физического вмешательства, человек на месте получает последовательность действий, а не просьбу наугад отключить всё питание.

    Вопросы и ответы

    Можно ли подключить объект с мобильным интернетом?

    Часто да. Сначала оцениваем покрытие, ограничения оператора и входящий доступ. Конкретную схему выбираем после проверки связи на площадке.

    Нужен ли постоянный специалист на месте?

    Для повседневной поддержки обычно достаточно ответственного сотрудника. Монтаж, замена оборудования и физические неисправности требуют присутствия человека.

    Можно ли сохранить имеющийся роутер?

    Да, если он поддерживает нужные функции и работает стабильно. Замены предлагаем по результатам обследования.

    Следующий шаг

    Расскажите, что должно работать

    Укажите ваш объект, существующее оборудование и задачу. Разберём исходные условия, уточним необходимые доступы и предложим порядок работ. Стоимость и сроки согласуем до начала.

    \ No newline at end of file diff --git a/public/assets/editorial/editorial.css b/public/assets/editorial/editorial.css new file mode 100644 index 0000000..200beb5 --- /dev/null +++ b/public/assets/editorial/editorial.css @@ -0,0 +1,12 @@ +:root{--ink:#eef2ff;--muted:#a8b9db;--cyan:#22d3ee;--blue:#3b82f6;--violet:#8b5cf6;--line:rgba(255,255,255,.12);--bg:#05070f;--panel:#0c1326;--max:1200px}*{box-sizing:border-box}html{scroll-behavior:smooth;scroll-padding-top:100px}body{margin:0;background:radial-gradient(ellipse at 10% 0,rgba(59,130,246,.13),transparent 45%),radial-gradient(ellipse at 100% 25%,rgba(139,92,246,.08),transparent 40%),var(--bg);color:var(--ink);font:16px/1.75 Inter,system-ui,sans-serif}a{color:inherit;text-decoration:none}a:focus-visible,summary:focus-visible{outline:3px solid var(--cyan);outline-offset:6px}img{max-width:100%;display:block}h1,h2,h3,.brand-name{font-family:'Space Grotesk',Inter,system-ui,sans-serif;line-height:1.15;letter-spacing:-.035em}h1{font-size:clamp(2.15rem,4.3vw,4.25rem);margin:20px 0 24px;max-width:960px}h2{font-size:clamp(1.55rem,2.3vw,2.25rem);margin:0 0 24px}h3{font-size:1.25rem}p{color:var(--muted);margin:0 0 20px}ul{padding-left:22px}.wrap{width:min(calc(100% - 48px),var(--max));margin:auto}.skip{position:fixed;top:-100px;left:24px;padding:12px;background:var(--cyan);color:#05070f;z-index:100}.skip:focus{top:8px}.site-nav{position:sticky;top:0;z-index:20;background:rgba(5,7,15,.94);border-bottom:1px solid var(--line);backdrop-filter:blur(15px)}.nav-row{display:flex;align-items:center;justify-content:space-between;gap:20px;min-height:82px}.brand{display:flex;align-items:center;gap:12px}.brand-orbit{display:grid;place-items:center;width:38px;height:38px;border-radius:12px;background:linear-gradient(135deg,var(--cyan),var(--blue),var(--violet));color:#071225;font-weight:800}.brand-name{font-size:20px;letter-spacing:-.03em}.brand small{display:block;font-size:10px;font-family:Inter,system-ui,sans-serif;letter-spacing:.03em;color:var(--muted);margin-top:5px}.nav-links{display:flex;align-items:center;gap:28px;font-size:14px}.nav-links>a:hover,.crumbs a:hover,.toc a:hover{color:var(--cyan)}.button{display:inline-flex;align-items:center;justify-content:center;padding:13px 22px;border-radius:12px;background:linear-gradient(120deg,var(--cyan),var(--blue));color:#051222;font-weight:700;font-size:14px;line-height:1.4;transition:transform .2s}.button:hover{transform:translateY(-2px)}.button.secondary{border:1px solid var(--line);background:rgba(255,255,255,.03);color:var(--ink)}.crumbs{display:flex;flex-wrap:wrap;gap:10px;list-style:none;padding:0;margin:0;font-size:12px;color:var(--muted)}.crumbs li+li:before{content:' / ';margin-right:10px;color:#53668d}.intro{padding:38px 0 50px}.eyebrow{display:inline-block;margin-top:38px;color:var(--cyan);font-size:11px;letter-spacing:.16em;text-transform:uppercase}.intro .lead{font-size:clamp(17px,1.6vw,21px);max-width:840px;line-height:1.7}.actions{display:flex;gap:12px;flex-wrap:wrap;margin-top:26px}.meta{font-size:12px;color:#92a5cd;margin-top:26px}.hero-figure{margin:0;position:relative;border:1px solid var(--line);border-radius:24px;overflow:hidden;background:#080c1a}.hero-figure img{width:100%;height:auto;aspect-ratio:3/2;object-fit:cover}.hero-figure figcaption{position:absolute;bottom:0;left:0;right:0;padding:35px 30px 22px;background:linear-gradient(transparent,rgba(5,7,15,.94));font-size:12px;color:#c1cde5}.strengths{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:var(--line);border:1px solid var(--line);border-radius:18px;overflow:hidden;margin:26px 0 72px}.strength{padding:26px;background:#080d1b;font-size:15px;line-height:1.6}.strength span{display:block;color:var(--cyan);font-size:11px;letter-spacing:.1em;margin-bottom:12px}.reading-grid{display:grid;grid-template-columns:250px minmax(0,1fr);gap:80px;align-items:start}.toc{position:sticky;top:112px;font-size:13px;line-height:1.6;border-top:1px solid var(--line);padding-top:20px}.toc strong{display:block;color:#fff;margin-bottom:16px}.toc ol{padding:0;list-style:none;display:grid;gap:14px;margin:0}.toc a{color:var(--muted)}.article-body{max-width:800px}.article-body section{margin:0 0 48px}.article-body section p{font-size:17px;line-height:1.9}.article-body section+section{border-top:1px solid var(--line);padding-top:38px}.faq{margin-top:60px}.faq details{border-bottom:1px solid var(--line);padding:20px 0}.faq summary{cursor:pointer;list-style:none;font-weight:600;padding-right:35px;position:relative;line-height:1.6}.faq summary::-webkit-details-marker{display:none}.faq summary:after{content:'+';position:absolute;right:4px;top:-2px;font-size:24px;color:var(--cyan)}.faq details[open] summary:after{content:'−'}.faq details p{padding-top:18px;margin-bottom:0;font-size:15px}.consult{border:1px solid rgba(34,211,238,.24);border-radius:24px;padding:clamp(24px,5vw,60px);margin:75px 0;background:radial-gradient(ellipse at right,rgba(59,130,246,.13),transparent 75%)}.consult p{max-width:760px}.consult .eyebrow{margin-top:0;margin-bottom:16px}.related{padding-bottom:80px}.related-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:24px}.related-card{border:1px solid var(--line);border-radius:18px;overflow:hidden;background:linear-gradient(140deg,rgba(255,255,255,.05),rgba(255,255,255,.01));transition:border-color .2s}.related-card:hover{border-color:var(--cyan)}.related-card img{aspect-ratio:3/2;object-fit:cover;width:100%;height:auto}.related-card .copy{padding:24px}.related-card h3{margin:0 0 12px}.related-card p{font-size:13px;margin-bottom:15px;line-height:1.7}.link-arrow{color:var(--cyan);font-size:13px;font-weight:600}.site-footer{border-top:1px solid var(--line);padding:35px 0 45px;font-size:13px;color:var(--muted)}.footer-row{display:flex;justify-content:space-between;gap:25px;flex-wrap:wrap}.site-footer a{color:var(--ink)}.footer-links{display:flex;gap:20px;flex-wrap:wrap}.hub-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:24px;margin:40px 0 70px}.hub-intro{max-width:820px}.card-landing{display:block;margin-top:22px;border-radius:12px;overflow:hidden;border:1px solid rgba(255,255,255,.12);position:relative;z-index:2}.card-landing img{display:block;width:100%;height:auto;aspect-ratio:3/2;object-fit:cover;transition:transform .3s}.card-landing span{display:block;padding:14px 15px;background:#0c1326;color:#67e8f9;font-size:13px;font-weight:600;line-height:1.4}.card-landing:hover img{transform:scale(1.025)}.card-landing:focus-visible{outline:3px solid #22d3ee;outline-offset:4px}.ind .card-landing{margin:0 24px 24px}.ind .top{display:none}.ind .body{padding-top:26px}.card p{flex-grow:0}.card-landing{margin-top:auto}.card h3+p{margin-bottom:22px}.card .stack{margin-bottom:20px}.content-note{font-size:12px;color:var(--muted)} +@media(max-width:900px){.reading-grid{grid-template-columns:1fr;gap:32px}.toc{position:static}.toc ol{grid-template-columns:1fr 1fr;display:grid;gap:12px 24px}.article-body{max-width:none}.nav-links{gap:18px}.nav-links .desktop-link{display:none}.related-grid,.hub-grid{gap:16px}.related-card .copy{padding:18px}.strengths{margin-bottom:45px}} +@media(max-width:600px){.wrap{width:calc(100% - 32px)}.nav-row{min-height:70px;gap:10px}.brand-name{font-size:16px}.brand small{font-size:8px}.brand-orbit{width:30px;height:30px;border-radius:9px}.nav-links{gap:8px}.nav-links .button{font-size:12px;padding:10px 12px}.intro{padding:24px 0 32px}.eyebrow{margin-top:30px;font-size:10px}.hero-figure{border-radius:16px}.hero-figure figcaption{position:static;padding:13px 16px;background:#080c1a;font-size:10px}.strengths{grid-template-columns:1fr}.strength{padding:20px}.strength span{margin-bottom:6px}.toc ol{grid-template-columns:1fr}.related-grid,.hub-grid{grid-template-columns:1fr}.article-body section p{font-size:16px}.article-body section{margin-bottom:32px}.consult{margin:45px 0}.actions .button{flex:1 1 160px}.meta{line-height:1.8}.related{padding-bottom:45px}.footer-links{gap:14px}.card-landing span{font-size:12px}} +@media(max-width:600px){.actions .button{flex-basis:100%;white-space:nowrap}} +.privacy-note{position:relative;margin:28px 0 22px;padding:22px 27px;border:1px solid rgba(249,185,108,.25);border-left:3px solid #e6ab70;border-radius:13px;background:linear-gradient(110deg,rgba(149,91,54,.17),rgba(255,255,255,.025));max-width:820px}.privacy-note span{display:block;color:#f2c598;font-size:11px;font-weight:700;letter-spacing:.13em;margin-bottom:8px}.privacy-note p{margin:0;color:#d5c8c0;font-size:15px;line-height:1.7}.hero-figure img{filter:saturate(.93) contrast(1.02)} +.article-top{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(350px,.85fr);gap:50px;align-items:start;padding-top:12px}.article-top .intro{padding:26px 0 18px}.article-top h1{font-size:clamp(2.15rem,3.35vw,3.55rem)}.article-top .hero-figure{margin:30px 0 0}.article-top .hero-figure img{aspect-ratio:3/2;object-fit:cover}.article-top .meta{margin-bottom:0} +@media(max-width:1000px){.article-top{grid-template-columns:1fr;gap:0}.article-top .intro{padding-bottom:16px}.article-top .hero-figure{margin:0}.article-top h1{font-size:clamp(2.15rem,4.3vw,4.25rem)}} +@media(max-width:600px){.privacy-note{padding:18px 20px;margin:20px 0}.privacy-note p{font-size:14px}} +@media(prefers-reduced-motion:reduce){html{scroll-behavior:auto}*,*:before,*:after{animation:none!important;transition:none!important}} +.system-flow{margin:-20px 0 55px;padding:26px;border:1px solid rgba(34,211,238,.21);border-radius:20px;background:radial-gradient(circle at 80% 0,rgba(34,211,238,.1),transparent 50%),#0a1121}.system-flow figcaption{display:grid;gap:4px;margin-bottom:25px}.flow-kicker{color:#67e8f9;font-size:10px;font-weight:700;letter-spacing:.15em}.system-flow figcaption strong{font:600 clamp(19px,2vw,23px)/1.35 'Space Grotesk',Inter,sans-serif;color:#f5f8ff}.system-flow figcaption small{font-size:12px;line-height:1.55;color:#a8b9db}.flow-track{display:flex;align-items:stretch;gap:0}.flow-device{position:relative;display:flex;flex:1;min-width:0;min-height:130px;flex-direction:column;justify-content:space-between;padding:16px;border:1px solid rgba(255,255,255,.13);border-radius:14px;background:rgba(255,255,255,.035)}.flow-device-top{display:flex;align-items:flex-start;justify-content:space-between}.flow-icon{display:grid;place-items:center;width:42px;height:42px;border-radius:12px;background:rgba(34,211,238,.13);color:#67e8f9}.flow-icon svg{width:25px;height:25px}.flow-led{width:9px;height:9px;border-radius:50%;background:#22c55e;box-shadow:0 0 0 3px rgba(34,197,94,.12);animation:flow-led-pulse 3.2s ease-in-out infinite}.flow-device:nth-child(3) .flow-led{animation-delay:.6s}.flow-device:nth-child(5) .flow-led{animation-delay:1.2s}.flow-device strong{font-size:13px;line-height:1.35;color:#eef2ff}.flow-device-index{position:absolute;right:14px;bottom:13px;color:#637799;font-size:10px}.flow-link{position:relative;flex:0 0 50px;align-self:center;height:2px;background:linear-gradient(90deg,rgba(34,211,238,.32),#67e8f9,rgba(34,211,238,.32))}.flow-link:before,.flow-link:after{content:'';position:absolute;right:-1px;top:-4px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:7px solid #67e8f9}.flow-link span{position:absolute;top:-3px;left:0;width:8px;height:8px;border-radius:50%;background:#fff;box-shadow:0 0 12px #67e8f9;animation:flow-packet 3.2s linear infinite}.flow-link:nth-child(4) span{animation-delay:1.6s}@keyframes flow-packet{0%{left:0;opacity:0}12%,85%{opacity:1}100%{left:calc(100% - 8px);opacity:0}}@keyframes flow-led-pulse{0%,100%{opacity:.4;box-shadow:0 0 0 2px rgba(34,197,94,.1)}45%{opacity:1;box-shadow:0 0 0 5px rgba(34,197,94,.17)}} +@media(max-width:600px){.system-flow{padding:20px;margin:0 0 42px}.flow-track{flex-direction:column}.flow-device{width:100%;min-height:84px;padding:12px 15px;flex-direction:row;align-items:center;gap:12px}.flow-icon{width:38px;height:38px}.flow-icon svg{width:23px;height:23px}.flow-device strong{flex:1}.flow-device-index{position:static}.flow-device-top{align-items:center}.flow-link{align-self:center;flex:0 0 28px;width:2px;height:28px;background:linear-gradient(180deg,rgba(34,211,238,.32),#67e8f9,rgba(34,211,238,.32))}.flow-link:before,.flow-link:after{right:-4px;top:auto;bottom:-1px;border-top:7px solid #67e8f9;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:0}.flow-link span{top:0;left:-3px;animation-name:flow-packet-vertical}@keyframes flow-packet-vertical{0%{top:0;opacity:0}12%,85%{opacity:1}100%{top:calc(100% - 8px);opacity:0}}} +@media(prefers-reduced-motion:reduce){.flow-link span{animation:none;opacity:1;left:50%;transform:translateX(-50%)}.flow-led{animation:none;opacity:1;box-shadow:0 0 0 3px rgba(34,197,94,.12)}} diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..0a596bd --- /dev/null +++ b/public/index.html @@ -0,0 +1,1676 @@ + + + + + + IT-поддержка бизнеса на КМВ и Юге России — servaki.online + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + +
    + + + + + + + +
    +
    +
    + + IT-поддержка для бизнеса на Юге России +

    Ваш IT-отдел на орбите

    +

    + Удалённое обслуживание компьютеров, сетей и серверов для малого и среднего бизнеса. + Решаем задачи без выездов — быстро, из любой точки, с фиксированной ценой подписки. +

    +

    >_ «Мы рядом, даже если между нами горы».

    + +
    +
    0
    компаний на поддержке
    +
    0
    среднее время реакции
    +
    24/7
    мониторинг систем
    +
    +
    + + +
    +
    + +
    + + +
    +
    +
    + О нас +

    Почему выбирают servaki.online

    +

    + Мы — удалённый IT-отдел для бизнеса Юга России. Держим инфраструктуру под контролем там, + где выезд специалиста дорог и долог: фермы, базы, объекты в горах, курорты и стройплощадки. +

    +
    + +
    +
    + +

    Реакция от 15 минут

    +

    При сбое связи, компьютера или сервиса принимаем заявку, оцениваем влияние на работу и начинаем диагностику. Отклик от 15 минут зависит от тарифа; срок устранения определяем после выяснения причины.

    + Рабочее место IT-поддержки и световая шкала времени в тёмно-синей палитреПодробнее: Реакция от 15 минут
    +
    + +

    Работаем с удалёнными точками

    +

    Связываем фермы, весовые, склады и горные объекты с офисом. Проверяем канал, питание и доступ к нужным системам, чтобы удалённая площадка оставалась управляемой.

    + Ферма, склад и горная база связаны голубыми линиями сети в кавказской долинеПодробнее: Работаем с удалёнными точками
    +
    + +

    Доступные подписки

    +

    Обслуживание рабочих мест и инфраструктуры по понятному ежемесячному плану: состав работ, ответственные и время отклика согласованы заранее. Помогаем не копить сбои до остановки работы.

    + Компьютер, сервер и роутер на упорядоченных модульных платформахПодробнее: Доступные подписки
    +
    +
    +
    + + +
    +
    +
    + Услуги +

    Что мы делаем

    +

    Полный цикл поддержки инфраструктуры — от рабочего места сотрудника до сервера и видеонаблюдения.

    +
    + +
    +
    + + +

    Удалённый рабочий стол

    +

    Помогаем сотруднику с компьютером, программой или принтером через согласованный сеанс. Сначала уточняем задачу, затем проверяем результат вместе с пользователем без лишнего доступа к данным.

    + Офисный компьютер и компактный принтер с защищённым каналом удалённой помощиПодробнее: Удалённый рабочий стол
    +
    + + +

    Сети и VPN

    +

    Строим связь между офисом, складом и удалённым объектом через защищённые соединения. Настраиваем маршруты и права доступа, проверяем прохождение трафика до рабочих сервисов.

    +
    WireGuardZeroTierKeenetic
    + Маршрутизатор и сетевой коммутатор соединяют несколько офисных миниатюр световыми линиямиПодробнее: Сети и VPN
    +
    + + +

    Мониторинг и безопасность

    +

    Следим за доступностью важных сервисов и сообщаем ответственным о сбоях. Состав мониторинга, резервное копирование и уведомления подбираем под инфраструктуру и порядок работы команды.

    + Панель наблюдения за серверами, сигналы доступности и защищённое резервное хранилищеПодробнее: Мониторинг и безопасность
    +
    + + +

    Видеонаблюдение

    +

    Подбираем камеры, запись и удалённый просмотр под задачи объекта. Учитываем зоны обзора, питание, хранение архива и права доступа, чтобы система помогала в работе.

    + IP-камера над складской площадкой и видеорегистратор в тёмной технологической композицииПодробнее: Видеонаблюдение
    +
    + + +

    Серверы и хостинг

    +

    Размещаем и сопровождаем серверы для рабочих приложений, чатов и внутренних сервисов. Контролируем доступы, обновления и резервные копии, согласуем перенос до начала работ.

    +
    ProxmoxDockerCRM · 1С
    + Компактная серверная стойка с синим и бирюзовым светом и орбитальной линией резервированияПодробнее: Серверы и хостинг
    +
    + + +

    Автоматизация

    +

    Соединяем датчики, учётные системы и уведомления в понятный рабочий процесс. Боты сообщают о событиях и собирают данные; управляющие команды доступны согласованным сотрудникам.

    + Промышленный контроллер, датчик и телефон соединены световыми потоками событийПодробнее: Автоматизация
    +
    +
    +
    + + +
    +
    +
    + Отрасли +

    Кому мы помогаем

    +

    Понимаем специфику бизнеса Юга России и подбираем решения под задачи каждой отрасли.

    +
    + +
    +
    + +
    +

    HoReCa

    +

    Для гостиниц и кафе проектируем разделение гостевого Wi-Fi и рабочих систем, помогаем с кассами и служебными сервисами. Доступ к данным гостей определяется правилами заведения.

    +
    + Уютный ресторан при горном отеле со стойкой администратора и деликатными линиями Wi-FiПодробнее: HoReCa
    +
    + +
    +

    Агросектор

    +

    На фермах и складах связываем датчики, весовые, камеры и удалённые площадки. Проверяем связь и питание, чтобы ответственные получали нужные показатели и понимали причину сбоя.

    +
    + Ферма у гор, весовая площадка и зерновое хранилище с деликатной визуализацией сети датчиковПодробнее: Агросектор
    +
    + +
    +

    Строительство

    +

    Организуем временные сети, рабочие места и камеры на стройплощадке. Учитываем перенос оборудования по этапам работ, питание, схему доступа и передачу настроек.

    +
    + Строительная площадка с краном, модульным офисом и камерой на фоне гор в ночной палитреПодробнее: Строительство
    +
    +
    +
    + + +
    +
    +
    + Как это работает +

    Четыре шага до решения

    +

    Простой и прозрачный процесс — от вашего сообщения до постоянного мониторинга. Как устроена поддержка →

    +
    + +
      +
    1. +
      1
      +

      Заявка

      +

      Пишете в Telegram, WhatsApp или звоните — фиксируем задачу.

      +
    2. +
    3. +
      2
      +

      Удалённое подключение

      +

      Безопасно подключаемся к вашему устройству или инфраструктуре.

      +
    4. +
    5. +
      3
      +

      Диагностика и решение

      +

      Находим причину и устраняем проблему, не дожидаясь выезда.

      +
    6. +
    7. +
      4
      +

      Отчёт и мониторинг

      +

      Присылаем отчёт и продолжаем следить за системами 24/7.

      +
    8. +
    +
    +
    + + +
    +
    +
    + Тарифы +

    Прозрачные подписки

    Что входит в обслуживание и как выбрать тариф →

    +

    Фиксированная цена в месяц. Прозрачные цены — без скрытых платежей и сюрпризов в счёте.

    +
    + +
    + + Стоимость указана при подписке от 3 месяцев — это минимальный период обслуживания. +
    + +
    +
    +
    Старт
    +
    9 000₽ / мес
    +
    Для небольшой команды
    +
    Цена при подписке от 3 месяцев
    +
      +
    • 1–3 рабочих места
    • +
    • Отклик до 2 часов
    • +
    • Удалённая поддержка
    • +
    • Без выездов
    • +
    + Выбрать «Старт» +
    + + + +
    +
    Премиум
    +
    45 000₽ / мес
    +
    Максимальный контроль и приоритет
    +
    Цена при подписке от 3 месяцев
    +
      +
    • Без ограничений по местам
    • +
    • Отклик 15 минут, 4 выезда/мес
    • +
    • Выделенный специалист
    • +
    • Видеонаблюдение, бэкапы, 24/7, приоритет
    • +
    + Выбрать «Премиум» +
    +
    + + +
    +
    15 мин
    первый отклик
    +
    99.5%
    аптайм систем
    +
    4 часа
    на критичные сбои
    +
    ∞
    без лимита обращений
    +
    +
    +
    + + + + + +
    +
    +
    + Связаться +

    Выведем ваш IT на орбиту

    +

    Напишите в мессенджер или оставьте заявку — ответим и предложим план поддержки.

    +
    + +
    +
    + +
    + +
    +
    Telegram
    + +
    +
    +
    + +
    +
    WhatsApp
    +
    +7 962 015-14-14Написать в WhatsApp
    +
    +
    +
    + +
    +
    Телефон
    +
    +7 962 015-14-14Telegram · WhatsApp · MAX
    +
    +
    +
    + +
    +
    Регион работы
    +
    Юг России · Ставропольский край · КМВ
    +
    +
    +
    + +
    +

    Оставить заявку

    +

    Опишите задачу — предложим подходящий тариф и план подключения.

    +
    +
    + + +
    Пожалуйста, укажите имя.
    +
    +
    + + +
    Укажите контакт для связи.
    +
    +
    + + +
    + +
    +

    Демонстрационная форма без отправки на сервер. Для реальных заявок подключается Telegram-бот / CRM.

    +
    +
    +
    +
    +
    + + + + +
    + + + + + + + diff --git a/public/industries/agriculture/index.html b/public/industries/agriculture/index.html new file mode 100644 index 0000000..492c46c --- /dev/null +++ b/public/industries/agriculture/index.html @@ -0,0 +1 @@ +IT для ферм и агробизнеса: связь, весовые и мониторинг | servaki.online
    Отрасли / Объекты далеко. Информация рядом.

    IT для ферм и агробизнеса: связь, весовые и мониторинг

    На ферме IT выходит за стены офиса: весовая, склад, ремонтная зона, камеры и датчики могут находиться на разных площадках. Помогаем связать их и видеть состояние важных систем.

    Команда servaki.online · · 3 мин чтения

    Семья фермеров и специалист обсуждают датчики и работу хозяйства
    Объекты далеко. Информация рядом. · Изображение создано ИИ, без данных и фотографий клиентов
    01 / НАШ ПОДХОДПрактика весовых и связи между площадками
    02 / НАШ ПОДХОДСочетание сетей, камер и контроллеров
    03 / НАШ ПОДХОДДиагностика с учётом питания и выездов

    Связь между площадками

    Проверяем доступные каналы, расстояния и зависимость оборудования от питания. Для офисной части, весовой и камер могут понадобиться разные требования к трафику. Подбираем схему удалённого доступа и резервирования с учётом существующего оборудования. Радиолинию и мобильный интернет оцениваем по измерениям на месте.

    ПРИНЦИП РАБОТЫПоказания с удалённого объектаУсловная схема движения сигнала, без реальных данных и настроек клиентов.
    Датчик01
    Связь02
    Ответственный03

    Весовая и рабочие данные

    Разделяем доступность сети, работу компьютера и поведение специализированной весовой программы. Уточняем интерфейсы оборудования и ответственность поставщика. Настраиваем согласованный доступ к рабочему месту, контроль связи и резервирование данных, если это входит в задачу. До интеграции с учётной системой проверяем формат обмена.

    Опыт на распределённых хозяйственных объектах

    В нашей практике есть диагностика связи весовой с удалённой ремонтной зоной и работа с видеонаблюдением на удалённых площадках. Наблюдение за доступностью оборудования и временем его работы позволяет точнее искать причину: перезапуск, пропадание питания или обрыв участка сети. Такой подход особенно полезен там, где каждый выезд занимает время.

    Показания и уведомления

    Работаем с контроллерами Wiren Board и передачей данных по MQTT. Можно организовать наблюдение за совместимыми измерителями, питанием и состоянием оборудования. Для каждого сигнала согласуем порог, период проверки и получателя. Управление исполнительными устройствами рассматриваем отдельно от мониторинга, с учётом локальных защит и условий эксплуатации.

    Результат для ответственного

    Стремимся сделать состояние площадки понятным без изучения десятков интерфейсов: какой объект на связи, откуда давно не поступали данные, что проверить первым. Документируем состав системы и действия при типовых отказах. Автоматизация внедряется поэтапно, начиная с самого полезного и проверяемого сценария.

    Вопросы и ответы

    Можно ли получать показания датчиков на телефон?

    Да, если датчики и контроллер поддерживают доступный интерфейс. Согласуем состав данных, периодичность и способ уведомлений.

    Подключите ли существующую весовую к учёту?

    Сначала проверим оборудование, программу и доступный формат обмена. Интеграция зависит от совместимости и документации поставщика.

    Что делать, если на площадке нестабильный интернет?

    Обследуем связь и питание, рассмотрим резерв. Критичные локальные функции должны сохраняться без зависимости от постоянного внешнего соединения.

    Следующий шаг

    Расскажите, что должно работать

    Укажите ваш объект, существующее оборудование и задачу. Разберём исходные условия, уточним необходимые доступы и предложим порядок работ. Стоимость и сроки согласуем до начала.

    \ No newline at end of file diff --git a/public/industries/construction/index.html b/public/industries/construction/index.html new file mode 100644 index 0000000..b62e0e2 --- /dev/null +++ b/public/industries/construction/index.html @@ -0,0 +1 @@ +IT для стройплощадок: временные сети и видеонаблюдение | servaki.online
    Отрасли / Инфраструктура движется вместе с объектом

    IT для стройплощадок: временные сети и видеонаблюдение

    Стройплощадка меняется по мере работ. Планируем связь и видеонаблюдение так, чтобы оборудование можно было обслуживать, переносить и расширять без потери понимания системы.

    Команда servaki.online · · 3 мин чтения

    Руководитель стройплощадки и специалист обсуждают оборудование в рабочем офисе
    Инфраструктура движется вместе с объектом · Изображение создано ИИ, без данных и фотографий клиентов
    01 / НАШ ПОДХОДОпыт работы с планами камер и трасс
    02 / НАШ ПОДХОДУчёт временного характера инфраструктуры
    03 / НАШ ПОДХОДДокументирование изменений и передачи объекта

    Интернет и рабочие места на объекте

    Организуем сеть для штаба строительства, сотрудников и согласованных рабочих систем. Выбор подключения зависит от доступных операторов, расположения площадки и срока эксплуатации. Проверяем питание, размещение оборудования и защищённый доступ к офисным ресурсам. Для временного решения также нужны документация и ответственный за обслуживание.

    ПРИНЦИП РАБОТЫСистема на строительной площадкеУсловная схема движения сигнала, без реальных данных и настроек клиентов.
    Камера01
    Сеть объекта02
    Штаб03

    Камеры под этап строительства

    Определяем нужные зоны: въезд, склад, периметр или ход работ. При изменении застройки прежний ракурс может стать бесполезным, поэтому предусматриваем пересмотр размещения. Учитываем кабельные трассы, питание, условия среды и сохранение архива. Удалённый просмотр проверяем отдельно от локальной записи.

    Практика работы со схемами

    В опыте команды есть работа с планами камер, кабельных трасс и систем доступа для строительного объекта. Наглядная модель помогает обсуждать обзор и расположение устройств, но не заменяет проверенную исполнительную документацию. Если исходные привязки отсутствуют, фиксируем это явно и уточняем их до монтажного решения.

    Изменения без потери управляемости

    Перенос бытовок, смена подрядчиков и добавление оборудования меняют сеть. После согласованных работ актуализируем схему и резервные конфигурации. Проверяем, что новые подключения не нарушили существующие камеры и рабочие места. Временные доступы выдаём под задачу и пересматриваем по завершении работ.

    Передача и дальнейшее сопровождение

    Составляем перечень устройств, назначение соединений и порядок восстановления после отключения питания. При передаче объекта важно передать не только оборудование, но и актуальные настройки, права и документацию. Заранее согласуем, что остаётся в постоянной эксплуатации, а что демонтируется или переносится на следующую площадку.

    Вопросы и ответы

    Можно ли начать с мобильного интернета?

    Да, если покрытие и нагрузка позволяют. Качество и возможность резерва оцениваем на площадке.

    Можно ли переносить камеры по мере строительства?

    Да, но после переноса нужно заново проверить обзор, питание, соединение и запись. План размещения стоит актуализировать.

    Заменяет ли 3D-схема монтажный проект?

    Нет. Наглядная схема помогает согласованию; точные привязки и монтажные решения требуют проверенных исходных данных и документации.

    Следующий шаг

    Расскажите, что должно работать

    Укажите ваш объект, существующее оборудование и задачу. Разберём исходные условия, уточним необходимые доступы и предложим порядок работ. Стоимость и сроки согласуем до начала.

    \ No newline at end of file diff --git a/public/industries/horeca/index.html b/public/industries/horeca/index.html new file mode 100644 index 0000000..8870506 --- /dev/null +++ b/public/industries/horeca/index.html @@ -0,0 +1 @@ +IT-поддержка гостиниц, кафе и ресторанов на КМВ | servaki.online
    Отрасли / Технологии поддерживают сервис

    IT-поддержка гостиниц, кафе и ресторанов на КМВ

    Гость должен пользоваться связью, а сотрудники — спокойно работать с заказами и бронированиями. Собираем IT-инфраструктуру заведения вокруг этих повседневных задач.

    Команда servaki.online · · 3 мин чтения

    Владелец семейного отеля и инженер проверяют связь в уютном ресторане
    Технологии поддерживают сервис · Изображение создано ИИ, без данных и фотографий клиентов
    01 / НАШ ПОДХОДРазделение гостевого и рабочего доступа
    02 / НАШ ПОДХОДДиагностика общих причин сбоев
    03 / НАШ ПОДХОДРаботы с учётом режима заведения

    Разделяем гостевую и служебную сеть

    Посетителям нужен удобный Wi-Fi, персоналу — стабильный доступ к рабочим системам. Предусматриваем разделение сегментов и ограничения доступа между ними. При обследовании учитываем планировку, перекрытия и места высокой нагрузки. Размещение точек доступа выбираем по условиям объекта, а качество связи проверяем в нужных зонах.

    ПРИНЦИП РАБОТЫСвязь в гостевом доме или кафеУсловная схема движения сигнала, без реальных данных и настроек клиентов.
    Гостевая сеть01
    Разделение02
    Рабочий сервис03

    Касса, бронирование и рабочее место

    Проверяем сетевую часть подключения кассовых устройств, принтеров, компьютеров администратора и внешних сервисов. Поддержку самого прикладного ПО и фискального оборудования согласуем с их поставщиками. Это помогает быстро определить, кто решает конкретную проблему, вместо пересылки заявки между несколькими подрядчиками.

    Как наш инженерный опыт помогает заведению

    Опыт команды в сетях, удалённых объектах, серверных сервисах и видеонаблюдении применим к гостиницам и ресторанам: здесь также важно видеть общие зависимости. Если пропало питание коммутатора, одновременно могут исчезнуть Wi-Fi, камеры и рабочие подключения. Начинаем диагностику с картины целиком, затем проверяем отдельные устройства.

    Обновления с учётом гостей и смен

    Согласуем работы на время минимальной нагрузки и предупреждаем, какие операции будут недоступны. Перед изменением сохраняем настройки и определяем порядок возврата. Для сезонных объектов полезно проверить оборудование и доступы до открытия, а после перестановок — заново оценить покрытие Wi-Fi и зоны обзора камер.

    Что обсуждаем перед подключением

    Количество зон и рабочих мест, часы работы, текущих поставщиков ПО, необходимость резерва интернета и удалённого просмотра камер. Интеграции с сервисами доставки или бронирования оцениваем по доступным интерфейсам. По результатам формируем понятный состав поддержки и правила обращения для персонала.

    Вопросы и ответы

    Можно ли улучшить Wi-Fi без полной замены сети?

    Возможно: сначала проверяем размещение точек, настройки, кабели и нагрузку. Замена нужна только там, где оборудование или схема ограничивают результат.

    Поддерживаете ли кассовое ПО?

    Согласуем перечень систем и границы работ. Настройку сети отделяем от задач, требующих участия поставщика кассового или фискального оборудования.

    Работаете ли с сезонными базами отдыха?

    Такой объект можно включить в сопровождение. Состав подготовки к сезону и условия поддержки согласуются по его оборудованию и режиму работы.

    Следующий шаг

    Расскажите, что должно работать

    Укажите ваш объект, существующее оборудование и задачу. Разберём исходные условия, уточним необходимые доступы и предложим порядок работ. Стоимость и сроки согласуем до начала.

    \ No newline at end of file diff --git a/public/industries/index.html b/public/industries/index.html new file mode 100644 index 0000000..cb794cb --- /dev/null +++ b/public/industries/index.html @@ -0,0 +1 @@ +IT-решения для отраслей | servaki.online
    Учитываем, как работает ваш бизнес

    IT-решения для отраслей

    IT-поддержка гостиниц и кафе, сельского хозяйства и строительных объектов: связь, рабочие системы, камеры и удалённый контроль на Юге России.

    \ No newline at end of file diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..ac95aba --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Allow: / + +Sitemap: https://servaki.online/sitemap.xml diff --git a/public/services/automation/index.html b/public/services/automation/index.html new file mode 100644 index 0000000..6502d7c --- /dev/null +++ b/public/services/automation/index.html @@ -0,0 +1 @@ +Автоматизация бизнеса: Telegram-боты, интеграции и контроллеры | servaki.online
    Услуги / Меньше ручных повторений

    Автоматизация бизнеса: Telegram-боты, интеграции и контроллеры

    Связываем источники данных и рабочие действия: получить показание, отправить уведомление, собрать отчёт или выполнить согласованную команду. Начинаем с одного полезного сценария.

    Команда servaki.online · · 3 мин чтения

    Специалист и руководитель хозяйства вместе осматривают датчик и телефон
    Меньше ручных повторений · Изображение создано ИИ, без данных и фотографий клиентов
    01 / НАШ ПОДХОДПрактика Wiren Board, MQTT и Telegram
    02 / НАШ ПОДХОДПроверка сценария от источника до получателя
    03 / НАШ ПОДХОДРазделение наблюдения и управляющих команд

    Сначала описываем рабочий процесс

    Разбираем, кто сейчас выполняет операцию, откуда берёт данные и кому передаёт результат. Выбираем измеримый сценарий: ежедневная сводка, уведомление об отклонении, сбор заявки или изменение статуса. Уточняем, что делать при недоступности источника, повторном событии и ошибке отправки.

    ПРИНЦИП РАБОТЫОт прибора до нужного действияУсловная схема движения сигнала, без реальных данных и настроек клиентов.
    Прибор01
    Контроллер02
    Уведомление03

    Боты и интеграции

    Создаём Telegram-ботов и связки между сервисами, когда у источников есть подходящий интерфейс. Интеграция с кассой, CRM или учётной системой требует проверки API, версии и прав доступа. Не обещаем совместимость со всем оборудованием: сначала подтверждаем, какие данные и действия реально доступны.

    Наш опыт: контроллеры и событийные сообщения

    В практике команды есть работа с Wiren Board, MQTT, энергетическими показателями и Telegram-уведомлениями. Измерение напряжения или мощности само по себе мало полезно владельцу; нужно добавить объект, время, понятный порог и объяснение события. Поэтому проектируем сообщение вокруг решения, которое сможет принять получатель.

    Команды требуют отдельной защиты

    Просмотр показаний и управление реле имеют разный уровень ответственности. Разделяем роли пользователей и разрешённые действия, предусматриваем подтверждение для чувствительных команд. Учитываем ограничения оборудования и локальные защитные механизмы. Автоматизация не должна обходить штатные блокировки и правила эксплуатации объекта.

    Проверяем весь сценарий

    Тестируем получение данных, обработку события, отправку и фактическое получение сообщения. Для отчётов проверяем свежесть данных и поведение при пропусках. Фиксируем зависимости от внешних сервисов и порядок восстановления. Развивать автоматизацию проще поэтапно: сначала надёжный сценарий, затем дополнительные источники и роли.

    Вопросы и ответы

    Можно ли автоматизировать отчёт из нашей системы?

    Сначала проверим доступный API, экспорт и права. Если данные можно получать устойчиво, предложим способ формирования и доставки отчёта.

    Можно ли управлять оборудованием из Telegram?

    Для совместимого оборудования — да, после согласования прав, подтверждений и защитных ограничений. Критичные блокировки должны оставаться на объекте.

    Что будет при недоступности Telegram?

    Это учитывается в проектировании: журнал событий, повторная доставка или резервный канал выбираются под важность сценария.

    Следующий шаг

    Расскажите, что должно работать

    Укажите ваш объект, существующее оборудование и задачу. Разберём исходные условия, уточним необходимые доступы и предложим порядок работ. Стоимость и сроки согласуем до начала.

    \ No newline at end of file diff --git a/public/services/index.html b/public/services/index.html new file mode 100644 index 0000000..88a3677 --- /dev/null +++ b/public/services/index.html @@ -0,0 +1 @@ +IT-услуги для бизнеса | servaki.online
    От рабочего места до инфраструктуры

    IT-услуги для бизнеса

    Удалённая помощь, сети и VPN, мониторинг, видеонаблюдение, серверы и автоматизация для бизнеса на КМВ и Юге России. Состав работ и порядок поддержки.

    \ No newline at end of file diff --git a/public/services/monitoring-security/index.html b/public/services/monitoring-security/index.html new file mode 100644 index 0000000..d5077a4 --- /dev/null +++ b/public/services/monitoring-security/index.html @@ -0,0 +1 @@ +Мониторинг IT-инфраструктуры и резервных копий бизнеса | servaki.online
    Услуги / Знать о проблеме и понимать действие

    Мониторинг IT-инфраструктуры и резервных копий бизнеса

    Настраиваем наблюдение за тем, от чего зависит работа бизнеса. Полезное уведомление должно отвечать на три вопроса: что произошло, где искать причину и кому действовать.

    Команда servaki.online · · 3 мин чтения

    Специалист и владелец бизнеса обсуждают наблюдение за системами и резервные копии
    Знать о проблеме и понимать действие · Изображение создано ИИ, без данных и фотографий клиентов
    01 / НАШ ПОДХОДКонтроль полезного сервиса, а не только адреса
    02 / НАШ ПОДХОДПроверка доставки уведомлений
    03 / НАШ ПОДХОДРезервирование с планом восстановления

    Что включаем в контроль

    Выбираем показатели под ваши сервисы: доступность сайта или приложения, состояние канала, свободное место, свежесть резервной копии и поступление данных от оборудования. Проверка одного сетевого адреса не заменяет проверку пользовательской операции. Для важных систем определяем, какой именно результат означает исправную работу.

    ПРИНЦИП РАБОТЫОт показателя до уведомленияУсловная схема движения сигнала, без реальных данных и настроек клиентов.
    Показатель01
    Мониторинг02
    Уведомление03

    Уведомления без постоянного шума

    Согласуем пороги, повторные проверки и получателей. Кратковременный сетевой сбой и длительная недоступность требуют разных действий. Уведомление дополняем названием объекта, наблюдаемым симптомом и следующей проверкой. Telegram может быть одним из каналов доставки; его работоспособность также нужно проверять.

    Сильная сторона: проверяем причину и восстановление

    В нашей практике мониторинг используется при поиске обрывов связи на удалённых площадках, наблюдении за серверами и сборе данных контроллеров. Сопоставляем доступность устройств и сервисов, чтобы локализовать участок отказа. После изменения проверяем исходный симптом; после настройки оповещений проводим согласованный пробный сценарий.

    Резервная копия должна восстанавливаться

    Само наличие файла ещё не доказывает пригодность резервной копии. Определяем, какие данные сохранять, как долго их хранить и где разместить копии. Согласуем допустимую потерю последних изменений и время восстановления. Для выбранных систем проводим пробное восстановление в отдельном месте, не затрагивая рабочие данные.

    Безопасность как регулярная работа

    Проверяем внешние точки входа, права учётных записей и актуальность обновлений. Изменения планируем с резервной копией и возможностью отката. Мониторинг не гарантирует отсутствие инцидентов: он помогает обнаруживать отклонения и действовать по согласованному порядку. Круглосуточный сбор сигналов и круглосуточное дежурство инженера оговариваются отдельно.

    Вопросы и ответы

    Что означает мониторинг 24/7?

    Автоматические проверки выполняются круглосуточно. Время реакции инженера, дежурство и эскалация определяются условиями обслуживания.

    Можно ли получать уведомления в Telegram?

    Да, если согласованы бот, получатели и правила доставки. После подключения проверяем отправку и получение тестового уведомления.

    Резервные копии исключают потерю данных?

    Нет. Результат зависит от периодичности, хранения и пригодности копий. Поэтому заранее определяем допустимые потери и проверяем восстановление.

    Следующий шаг

    Расскажите, что должно работать

    Укажите ваш объект, существующее оборудование и задачу. Разберём исходные условия, уточним необходимые доступы и предложим порядок работ. Стоимость и сроки согласуем до начала.

    \ No newline at end of file diff --git a/public/services/networks-vpn/index.html b/public/services/networks-vpn/index.html new file mode 100644 index 0000000..0d97274 --- /dev/null +++ b/public/services/networks-vpn/index.html @@ -0,0 +1 @@ +Сети и VPN для бизнеса: офисы, склады и удалённые точки | servaki.online
    Услуги / Одна команда. Несколько площадок.

    Сети и VPN для бизнеса: офисы, склады и удалённые точки

    Связываем офисы, склады и удалённые объекты так, чтобы сотрудники получали доступ к нужным ресурсам, а управление сетью оставалось понятным и контролируемым.

    Команда servaki.online · · 3 мин чтения

    Инженер обсуждает офисную сеть с командой семейного склада
    Одна команда. Несколько площадок. · Изображение создано ИИ, без данных и фотографий клиентов
    01 / НАШ ПОДХОДПрактика Keenetic, MikroTik и распределённых VPN
    02 / НАШ ПОДХОДПроверка прямого и обратного маршрута
    03 / НАШ ПОДХОДУчёт общих зависимостей резервных каналов

    Начинаем с задач доступа

    Уточняем, кому и к чему нужна связь: сотрудникам — к файлам и приложениям, ответственному — к камерам, инженеру — к оборудованию. Проверяем адресацию, ограничения провайдеров и существующие маршруты. Это помогает заранее обнаружить пересекающиеся подсети и избежать ситуации, когда подключение VPN лишает объект обычного интернета.

    ПРИНЦИП РАБОТЫКак проходит трафик между площадкамиУсловная схема движения сигнала, без реальных данных и настроек клиентов.
    Роутер объекта01
    Защищённый канал02
    Ресурс компании03

    Подбираем схему под площадку

    Работаем с маршрутизаторами Keenetic и MikroTik, туннелями WireGuard и решениями на базе ZeroTier. Выбор зависит от оборудования, числа точек и требований к доступу. Разделяем служебный и гостевой трафик, задаём необходимые правила доступа и предусматриваем способ восстановления управления после изменения конфигурации.

    Наш опыт: соединение проверяется трафиком

    В распределённых сетях встречается ситуация: туннель показывает активное состояние, но полезные данные не проходят. В нашей практике такие случаи разбираются по маршрутам в обе стороны, счётчикам обмена и доступности целевого сервиса. Отдельно проверяем DNS и сохранение настроек. Это позволяет искать причину точнее, чем по одному зелёному индикатору.

    Резервирование без ложной уверенности

    Два подключения не всегда означают два независимых канала: они могут зависеть от одного питания, линии или промежуточного узла. Фиксируем общие зависимости и проверяем поведение при отказе. Согласуем, какие сервисы должны пережить переключение и допустимо ли краткое прерывание работы.

    Что передаём после настройки

    Описываем назначение сегментов и соединений, сохраняем резервную конфигурацию и порядок отката. Проверяем доступ с рабочих мест и удалённых площадок. При сопровождении наблюдаем за нужными каналами и сервисами; новые правила вводим с учётом уже работающих устройств.

    Вопросы и ответы

    Для чего бизнесу VPN?

    Для защищённого соединения площадок и доступа к корпоративным ресурсам. Состав доступных систем и права пользователей задаются отдельно.

    Можно ли объединить офис и склад у разных провайдеров?

    Да, если связь и оборудование позволяют построить выбранную схему. Проверяем ограничения, адресацию и качество канала.

    VPN сам обеспечивает резервный интернет?

    Нет. Для резерва нужен дополнительный рабочий канал и настроенное переключение. VPN решает задачу связи поверх доступного соединения.

    Следующий шаг

    Расскажите, что должно работать

    Укажите ваш объект, существующее оборудование и задачу. Разберём исходные условия, уточним необходимые доступы и предложим порядок работ. Стоимость и сроки согласуем до начала.

    \ No newline at end of file diff --git a/public/services/remote-desktop/index.html b/public/services/remote-desktop/index.html new file mode 100644 index 0000000..02cabc0 --- /dev/null +++ b/public/services/remote-desktop/index.html @@ -0,0 +1 @@ +Удалённая компьютерная помощь для бизнеса на КМВ | servaki.online
    Услуги / Рабочее место снова работает

    Удалённая компьютерная помощь для бизнеса на КМВ

    Помогаем сотруднику вернуться к работе: разобраться с программой, восстановить доступ, настроить принтер или подготовить рабочее место. Подключение и объём действий согласуем заранее.

    Команда servaki.online · · 3 мин чтения

    Специалист объясняет работу компьютера владелице небольшого магазина
    Рабочее место снова работает · Изображение создано ИИ, без данных и фотографий клиентов
    01 / НАШ ПОДХОДДиагностика от приложения до сети и сервера
    02 / НАШ ПОДХОДСогласованные действия с рабочими данными
    03 / НАШ ПОДХОДПроверка результата вместе с сотрудником

    Какие задачи решаем удалённо

    Диагностируем ошибки операционной системы и приложений, настраиваем сетевые принтеры, доступ к общим папкам и рабочим сервисам. Помогаем подключить новое рабочее место к существующей инфраструктуре. Перед изменениями уточняем, какие данные и программы критичны для сотрудника и что уже предпринималось для решения проблемы.

    ПРИНЦИП РАБОТЫПомощь с рабочего местаУсловная схема движения сигнала, без реальных данных и настроек клиентов.
    Компьютер01
    Согласованный сеанс02
    Специалист03

    Как проходит сеанс помощи

    Сотрудник описывает симптом и подтверждает подключение. Проверяем состояние компьютера, сообщения об ошибках, сетевые настройки и доступность зависимых сервисов. При необходимости согласуем перезапуск или временное закрытие программы. Завершаем сеанс проверкой конкретной операции, которая не работала, и кратким объяснением результата.

    Наш подход: компьютер — часть инфраструктуры

    Ошибка на экране не всегда возникает на самом ПК. Приложение может зависеть от VPN, файлового сервера или службы авторизации. Опыт сопровождения сетей и серверов помогает проверять цепочку целиком. Это особенно важно для удалённых сотрудников и объектов, где рабочее место выходит в корпоративные ресурсы через несколько участков связи.

    Безопасность доступа

    Выбираем согласованный инструмент удалённой помощи и определяем, кто вправе подключаться. Постоянный доступ настраиваем только при необходимости, с ограничением прав. Учётные записи закрепляем за ответственными, а доступ уволенных сотрудников отзываем. Перед действиями, способными затронуть данные, проверяем наличие подходящей резервной копии.

    Границы удалённой помощи

    Неисправный накопитель, повреждённый кабель и механическая поломка принтера требуют работ на месте. Если диагностика указывает на аппаратную причину, объясняем, что именно нужно проверить или заменить. Для специфического прикладного ПО при необходимости подключаем его поставщика: сетевую доступность и работу самой программы важно разделять.

    Вопросы и ответы

    Можно ли помочь сотруднику вне офиса?

    Да, если есть рабочий интернет и возможность согласованного удалённого подключения. Доступ к корпоративным ресурсам проверяем отдельно.

    Нужно ли сообщать пароль от всех сервисов?

    Нет. Согласуем только необходимый доступ и подходящий способ его предоставления. Лишние права для диагностики не нужны.

    Настраиваете ли сетевые принтеры?

    Да: проверяем сетевое подключение, очередь печати и драйвер. Механический ремонт и расходные материалы относятся к отдельным работам.

    Следующий шаг

    Расскажите, что должно работать

    Укажите ваш объект, существующее оборудование и задачу. Разберём исходные условия, уточним необходимые доступы и предложим порядок работ. Стоимость и сроки согласуем до начала.

    \ No newline at end of file diff --git a/public/services/servers-hosting/index.html b/public/services/servers-hosting/index.html new file mode 100644 index 0000000..ce9eac3 --- /dev/null +++ b/public/services/servers-hosting/index.html @@ -0,0 +1 @@ +Серверы и хостинг для бизнеса: Proxmox, Docker и сопровождение | servaki.online
    Услуги / Основа для ежедневной работы

    Серверы и хостинг для бизнеса: Proxmox, Docker и сопровождение

    Размещаем рабочие сервисы там, где ими удобно управлять и восстанавливать их после сбоя. Выбор между локальным сервером и хостингом начинаем с требований к данным и доступности.

    Команда servaki.online · · 3 мин чтения

    Инженер проверяет серверное оборудование вместе с владельцем бизнеса
    Основа для ежедневной работы · Изображение создано ИИ, без данных и фотографий клиентов
    01 / НАШ ПОДХОДПрактика Proxmox, Docker и серверных приложений
    02 / НАШ ПОДХОДУчёт данных и зависимостей при переносе
    03 / НАШ ПОДХОДОбновления с проверкой и планом отката

    От задачи бизнеса к размещению

    Уточняем состав приложений, пользователей и данных: сайт, CRM, файловое хранилище, внутренняя система или другое рабочее ПО. Проверяем требования поставщиков и лицензирования. Оцениваем ресурсы, зависимость от интернета и возможный простой. Это помогает подобрать размещение без лишней сложности и без недооценки нагрузки.

    ПРИНЦИП РАБОТЫРабота и восстановление сервераУсловная схема движения сигнала, без реальных данных и настроек клиентов.
    Сервер01
    Резервная копия02
    Восстановление03

    Виртуализация и контейнеры

    Используем Proxmox для разделения серверных задач на виртуальные машины и контейнеры, Docker — для подходящих приложений. Разделение упрощает обслуживание, но само по себе не создаёт отказоустойчивость. Планируем хранение данных, доступ к управлению, обновления и резервное копирование каждого значимого компонента.

    Практический опыт сопровождения

    В нашей инфраструктурной практике есть виртуальные машины, контейнерные приложения, базы данных и веб-сервисы. Поэтому рассматриваем не только запуск программы, но и её зависимости: домен, сертификат, база, хранилище, почта и фоновые задачи. При переносе важно проверить пользовательский сценарий и сохранность данных, а не только состояние процесса.

    Обновления с возможностью вернуться назад

    Перед изменением определяем состав резервной копии и порядок отката. Для важных обновлений используем отдельную проверку или тестовый контур, если он предусмотрен. После работ проверяем ключевые функции приложения и журналы ошибок. Документируем версии и особенности конфигурации, которые понадобятся при следующем обслуживании.

    Что входит в понятное сопровождение

    Фиксируем ответственных за хостинг, приложение, лицензии и резервные копии. Настраиваем контроль ресурсов и важных сроков, включая сертификаты при включении этой задачи в обслуживание. Отдельно согласуем перенос данных, администрирование прикладной системы и доработку её кода: это разные виды работ.

    Вопросы и ответы

    Что лучше: сервер в офисе или хостинг?

    Зависит от приложений, связи, требований к данным и допустимого простоя. Сначала сравниваем эти условия и стоимость сопровождения.

    Делаете ли перенос существующего сервиса?

    Да, после обследования и согласования окна работ. План включает копирование данных, проверку, переключение и возможность отката.

    Нужны ли копии, если есть виртуализация?

    Да. Виртуализация не заменяет независимые резервные копии. Снимок на том же хранилище не защищает от его отказа.

    Следующий шаг

    Расскажите, что должно работать

    Укажите ваш объект, существующее оборудование и задачу. Разберём исходные условия, уточним необходимые доступы и предложим порядок работ. Стоимость и сроки согласуем до начала.

    \ No newline at end of file diff --git a/public/services/video-surveillance/index.html b/public/services/video-surveillance/index.html new file mode 100644 index 0000000..86fbb62 --- /dev/null +++ b/public/services/video-surveillance/index.html @@ -0,0 +1 @@ +Видеонаблюдение для бизнеса: камеры, архив и удалённый доступ | servaki.online
    Услуги / Нужный ракурс. Доступная запись.

    Видеонаблюдение для бизнеса: камеры, архив и удалённый доступ

    Помогаем видеть нужную зону и находить запись нужного события. Рассматриваем видеонаблюдение как единую систему: обзор, освещение, сеть, питание, регистратор и доступ ответственных.

    Команда servaki.online · · 3 мин чтения

    Инженер и владельцы гостевого дома оценивают обзор наружной камеры
    Нужный ракурс. Доступная запись. · Изображение создано ИИ, без данных и фотографий клиентов
    01 / НАШ ПОДХОДОпыт с сетевой частью и удалёнными регистраторами
    02 / НАШ ПОДХОДПроверка архива вместе с живым изображением
    03 / НАШ ПОДХОДНаглядное обсуждение ракурсов и трасс

    Начинаем с того, что нужно увидеть

    Для въезда, кассовой зоны и общего обзора территории нужны разные ракурсы. Уточняем задачу каждой камеры, расстояние до объекта наблюдения и условия освещения. При проектировании учитываем высоту установки, препятствия, кабельные трассы и возможность обслуживания. Количество камер само по себе не определяет качество системы.

    ПРИНЦИП РАБОТЫКак работает видеонаблюдениеУсловная схема движения сигнала, без реальных данных и настроек клиентов.
    Камера01
    Локальная запись02
    Просмотр03

    Сеть и питание — часть видеонаблюдения

    Пропадание изображения может быть связано с камерой, PoE-коммутатором, радиомостом или перегрузкой канала. Проверяем цепочку целиком. Для удалённых площадок отдельно оцениваем пропускную способность и доступ к локальному архиву, чтобы просмотр через интернет не мешал основной работе объекта.

    Наш опыт: от трасс до удалённой диагностики

    В практике команды есть работа со схемами камер и кабельных трасс на строительных объектах, а также диагностика регистраторов и связи между удалёнными площадками. Используем планы и наглядные схемы для обсуждения зон обзора. Условную визуализацию всегда отделяем от точной монтажной документации и проверяем привязки по исходным материалам.

    Архив проверяем отдельно от живого видео

    Открывающаяся камера ещё не означает, что запись сохраняется. Проверяем дату и время, режим записи, состояние накопителей и поиск фрагмента. Срок хранения зависит от числа камер, разрешения, интенсивности записи и ёмкости дисков. Рассчитываем его под задачу и подтверждаем на выбранной конфигурации.

    Доступ для владельца и ответственных

    Разделяем права просмотра и администрирования, согласуем устройства для удалённого доступа. После настройки проверяем соединение из нужной сети и просмотр архива. Изменения паролей и состава пользователей учитываем в сопровождении. Для расширения системы заранее оцениваем запас по портам, питанию и хранилищу.

    Вопросы и ответы

    Можно ли оставить существующие камеры?

    Да, если совместимость, состояние и качество изображения подходят задаче. Сначала обследуем систему и выявляем ограничения.

    Будет ли запись при пропадании интернета?

    При локальном регистраторе запись может продолжаться, если питание и локальная сеть исправны. Это нужно проверить на конкретной системе.

    Можно ли смотреть камеры с телефона?

    Да, для совместимого оборудования настраиваем согласованный способ доступа и права. Работу проверяем через сеть, которой пользуется ответственный.

    Следующий шаг

    Расскажите, что должно работать

    Укажите ваш объект, существующее оборудование и задачу. Разберём исходные условия, уточним необходимые доступы и предложим порядок работ. Стоимость и сроки согласуем до начала.

    \ No newline at end of file diff --git a/public/sitemap.xml b/public/sitemap.xml new file mode 100644 index 0000000..717271f --- /dev/null +++ b/public/sitemap.xml @@ -0,0 +1,2 @@ + +https://servaki.online/https://servaki.online/approach/https://servaki.online/approach/it-subscription/https://servaki.online/approach/rapid-response/https://servaki.online/approach/remote-sites/https://servaki.online/industries/https://servaki.online/industries/agriculture/https://servaki.online/industries/construction/https://servaki.online/industries/horeca/https://servaki.online/services/https://servaki.online/services/automation/https://servaki.online/services/monitoring-security/https://servaki.online/services/networks-vpn/https://servaki.online/services/remote-desktop/https://servaki.online/services/servers-hosting/https://servaki.online/services/video-surveillance/ \ No newline at end of file diff --git a/reference/original.html b/reference/original.html new file mode 100644 index 0000000..611b2b1 --- /dev/null +++ b/reference/original.html @@ -0,0 +1,1676 @@ + + + + + + servaki.online — IT-поддержка для бизнеса на Юге России | Ваш IT-отдел на орбите + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + +
    + + + + + + + +
    +
    +
    + + IT-поддержка для бизнеса на Юге России +

    Ваш IT-отдел на орбите

    +

    + Удалённое обслуживание компьютеров, сетей и серверов для малого и среднего бизнеса. + Решаем задачи без выездов — быстро, из любой точки, с фиксированной ценой подписки. +

    +

    >_ «Мы рядом, даже если между нами горы».

    + +
    +
    0
    компаний на поддержке
    +
    0
    среднее время реакции
    +
    24/7
    мониторинг систем
    +
    +
    + + +
    +
    + +
    + + +
    +
    +
    + О нас +

    Почему выбирают servaki.online

    +

    + Мы — удалённый IT-отдел для бизнеса Юга России. Держим инфраструктуру под контролем там, + где выезд специалиста дорог и долог: фермы, базы, объекты в горах, курорты и стройплощадки. +

    +
    + +
    +
    + +

    Реакция от 15 минут

    +

    Решаем большинство задач удалённо, без выездов через перевалы. Помощь приходит по подключению, а не по дороге.

    +
    +
    + +

    Работаем с удалёнными точками

    +

    Фермы, весовые, склады, базы и объекты в горах — объединяем их в единую защищённую сеть и держим на связи.

    +
    +
    + +

    Доступные подписки

    +

    Фиксированная цена в месяц вместо непредсказуемых счетов за разовые вызовы. Бюджет на IT понятен заранее.

    +
    +
    +
    +
    + + +
    +
    +
    + Услуги +

    Что мы делаем

    +

    Полный цикл поддержки инфраструктуры — от рабочего места сотрудника до сервера и видеонаблюдения.

    +
    + +
    +
    + + +

    Удалённый рабочий стол

    +

    Диагностика и настройка ПК, программного обеспечения, драйверов и принтеров без выезда специалиста.

    +
    +
    + + +

    Сети и VPN

    +

    Объединяем офисы, склады и удалённые точки в единую защищённую сеть.

    +
    WireGuardZeroTierKeenetic
    +
    +
    + + +

    Мониторинг и безопасность

    +

    Круглосуточный контроль серверов, регулярные резервные копии и антивирусная защита.

    +
    +
    + + +

    Видеонаблюдение

    +

    IP-камеры, регистраторы, проектирование трасс и удалённый доступ к архиву.

    +
    +
    + + +

    Серверы и хостинг

    +

    Разворачиваем и обслуживаем сервисы для бизнеса: почта, чаты, CRM и 1С.

    +
    ProxmoxDockerCRM · 1С
    +
    +
    + + +

    Автоматизация

    +

    Telegram-боты и интеграции с кассами: уведомления, отчёты и рутинные операции без ручного труда.

    +
    +
    +
    +
    + + +
    +
    +
    + Отрасли +

    Кому мы помогаем

    +

    Понимаем специфику бизнеса Юга России и подбираем решения под задачи каждой отрасли.

    +
    + +
    +
    +
    HoReCa — IT для ресторанов и кафе: кассы, Wi-Fi, сеть
    +
    +

    HoReCa

    +

    Кассы и POS-терминалы, стабильный Wi-Fi для гостей, интеграция с Яндекс.Еда и Delivery Club.

    +
    +
    +
    +
    Агросектор — IT для сельского хозяйства: сети, весовые, датчики
    +
    +

    Агросектор

    +

    Сети удалённых точек, весовые, видеонаблюдение и датчики на фермах, полях и складах.

    +
    +
    +
    +
    Строительство — IT для стройплощадок: временные сети, камеры, учёт
    +
    +

    Строительство

    +

    Временные сети и роутеры на объектах, камеры для контроля и учёт материалов.

    +
    +
    +
    +
    +
    + + +
    +
    +
    + Как это работает +

    Четыре шага до решения

    +

    Простой и прозрачный процесс — от вашего сообщения до постоянного мониторинга.

    +
    + +
      +
    1. +
      1
      +

      Заявка

      +

      Пишете в Telegram, WhatsApp или звоните — фиксируем задачу.

      +
    2. +
    3. +
      2
      +

      Удалённое подключение

      +

      Безопасно подключаемся к вашему устройству или инфраструктуре.

      +
    4. +
    5. +
      3
      +

      Диагностика и решение

      +

      Находим причину и устраняем проблему, не дожидаясь выезда.

      +
    6. +
    7. +
      4
      +

      Отчёт и мониторинг

      +

      Присылаем отчёт и продолжаем следить за системами 24/7.

      +
    8. +
    +
    +
    + + +
    +
    +
    + Тарифы +

    Прозрачные подписки

    +

    Фиксированная цена в месяц. Прозрачные цены — без скрытых платежей и сюрпризов в счёте.

    +
    + +
    + + Стоимость указана при подписке от 3 месяцев — это минимальный период обслуживания. +
    + +
    +
    +
    Старт
    +
    3000₽ / мес
    +
    Для небольшой команды
    +
    Цена при подписке от 3 месяцев
    +
      +
    • 1–3 рабочих места
    • +
    • Отклик до 2 часов
    • +
    • Удалённая поддержка
    • +
    • Без выездов
    • +
    + Выбрать «Старт» +
    + + + +
    +
    Премиум
    +
    15000₽ / мес
    +
    Максимальный контроль и приоритет
    +
    Цена при подписке от 3 месяцев
    +
      +
    • Без ограничений по местам
    • +
    • Отклик 15 минут, 4 выезда/мес
    • +
    • Выделенный специалист
    • +
    • Видеонаблюдение, бэкапы, 24/7, приоритет
    • +
    + Выбрать «Премиум» +
    +
    + + +
    +
    15 мин
    первый отклик
    +
    99.5%
    аптайм систем
    +
    4 часа
    на критичные сбои
    +
    ∞
    без лимита обращений
    +
    +
    +
    + + + + + +
    +
    +
    + Связаться +

    Выведем ваш IT на орбиту

    +

    Напишите в мессенджер или оставьте заявку — ответим и предложим план поддержки.

    +
    + +
    +
    + +
    + +
    +
    Telegram
    + +
    +
    +
    + +
    +
    WhatsApp
    +
    +7 962 015-14-14Написать в WhatsApp
    +
    +
    +
    + +
    +
    Телефон
    +
    +7 962 015-14-14Telegram · WhatsApp · MAX
    +
    +
    +
    + +
    +
    Регион работы
    +
    Юг России · Ставропольский край · КМВ
    +
    +
    +
    + +
    +

    Оставить заявку

    +

    Опишите задачу — предложим подходящий тариф и план подключения.

    +
    +
    + + +
    Пожалуйста, укажите имя.
    +
    +
    + + +
    Укажите контакт для связи.
    +
    +
    + + +
    + +
    +

    Демонстрационная форма без отправки на сервер. Для реальных заявок подключается Telegram-бот / CRM.

    +
    +
    +
    +
    +
    + + + + +
    + + + + + + + diff --git a/reference/public-assets.json b/reference/public-assets.json new file mode 100644 index 0000000..5868402 --- /dev/null +++ b/reference/public-assets.json @@ -0,0 +1,30 @@ +[ + { + "path": "/img/construction.png", + "bytes": 2133417 + }, + { + "path": "/img/farms.png", + "bytes": 2438466 + }, + { + "path": "/img/mountains.png", + "bytes": 2557280 + }, + { + "path": "/img/office.png", + "bytes": 1980826 + }, + { + "path": "/img/resorts.png", + "bytes": 2456335 + }, + { + "path": "/img/restaurant.png", + "bytes": 2383609 + }, + { + "path": "/img/servers.png", + "bytes": 1939634 + } +] \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..1b31a3e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +Pillow==12.3.0 +paramiko==5.0.0 +cryptography==50.0.1 diff --git a/verify.py b/verify.py new file mode 100644 index 0000000..994a184 --- /dev/null +++ b/verify.py @@ -0,0 +1,106 @@ +"""Validate rendered site, links, structured data, images and served responses.""" +from pathlib import Path +from html.parser import HTMLParser +from urllib.parse import urlsplit,unquote +from urllib.request import build_opener,ProxyHandler +from xml.etree import ElementTree as ET +import json,re,hashlib +ROOT=Path(__file__).resolve().parent;PUBLIC=ROOT/'public' +class Page(HTMLParser): + def __init__(self,text): + super().__init__(convert_charrefs=True) + self.h1=0;self.links=[];self.images=[];self.ids=[];self.ld=[];self.title='';self.description='';self.canonical='';self.script=None;self.in_title=False + self.feed(text) + def handle_starttag(self,tag,attrs): + a=dict(attrs) + if 'id' in a:self.ids.append(a['id']) + if tag=='h1':self.h1+=1 + if tag=='title':self.in_title=True + if tag=='a' and a.get('href'):self.links.append(a['href']) + if tag=='link' and a.get('rel')=='stylesheet':self.links.append(a['href']) + if tag=='link' and a.get('rel')=='canonical':self.canonical=a['href'] + if tag=='meta' and a.get('name')=='description':self.description=a.get('content','') + if tag=='img' and a.get('src'): + self.images.append(a) + self.links.append(a['src']) + self.links += [part.strip().split()[0] for part in a.get('srcset','').split(',') if part.strip()] + if tag=='meta' and a.get('property')=='og:image':self.links.append(a['content']) + if tag=='script' and a.get('type')=='application/ld+json':self.script='' + def handle_endtag(self,tag): + if tag=='title':self.in_title=False + if tag=='script' and self.script is not None: + self.ld.append(json.loads(self.script));self.script=None + def handle_data(self,d): + if self.in_title:self.title+=d + if self.script is not None:self.script+=d +def check(text,rel): + p=Page(text) + assert p.h1==1,(rel,'expected one H1') + assert p.title and p.description and p.canonical,(rel,'missing metadata') + assert len(set(p.ids))==len(p.ids),(rel,'duplicate IDs') + assert p.canonical=='https://servaki.online/'+('' if rel=='index.html' else rel.removesuffix('index.html')),(rel,'canonical') + for link in p.links: + u=urlsplit(link) + if u.scheme and u.scheme not in ('http','https'):continue + if u.netloc and u.netloc!='servaki.online':continue + target=PUBLIC/u.path.lstrip('/') if u.path.startswith('/') else PUBLIC/Path(rel).parent/u.path + if not u.path:target=PUBLIC/rel + if target.is_dir():target=target/'index.html' + assert target.is_file(),(rel,'missing link',link) + if u.fragment and target.suffix=='.html': + assert unquote(u.fragment) in Page(target.read_text(encoding='utf-8')).ids,(rel,'missing fragment',link) + for im in p.images: + assert 'alt' in im,(rel,'missing image alt') + if not im['src'].startswith('https://mc.yandex.ru/'): + assert im.get('width') and im.get('height'),(rel,'missing dimensions') + return p +def main(): + files=sorted(PUBLIC.rglob('*.html'));titles=[];descriptions=[];checks=[] + scenes=json.loads((ROOT/'content/flow-scenes.json').read_text(encoding='utf-8')) + pages=json.loads((ROOT/'content/pages.json').read_text(encoding='utf-8')) + card_copy=json.loads((ROOT/'content/card-copy.json').read_text(encoding='utf-8')) + assert set(scenes)=={p['slug'] for p in pages},'missing or extra equipment scene' + assert set(card_copy)==set(scenes),'missing or extra homepage card description' + css=(PUBLIC/'assets/editorial/editorial.css').read_text(encoding='utf-8') + assert '@media(prefers-reduced-motion:reduce){.flow-link span{animation:none' in css,'missing static motion alternative' + opener=build_opener(ProxyHandler({})) + for file in files: + rel=file.relative_to(PUBLIC).as_posix();text=file.read_text(encoding='utf-8');p=check(text,rel) + if rel!='index.html' and rel.count('/')==2: + slug=Path(rel).parent.name + assert text.count('class="system-flow"')==1,(rel,'equipment diagram') + assert text.count('class="flow-device"')==3 and text.count('class="flow-link"')==2,(rel,'device and traffic path') + assert scenes[slug]['title'] in text,(rel,'wrong diagram') + photo=next((im for im in p.images if im['src'].startswith('/assets/editorial/photo-')),None) + assert photo and '520px' in photo.get('sizes',''),(rel,'responsive article photograph') + titles.append(p.title);descriptions.append(p.description) + route='/'+('' if rel=='index.html' else rel.removesuffix('index.html')) + served=opener.open('http://127.0.0.1:5186'+route,timeout=10) + assert served.status==200 and served.read()==file.read_bytes(),(rel,'HTTP mismatch') + checks.append({'path':route,'http':200,'h1':p.h1,'images':len(p.images),'schema_blocks':len(p.ld),'sha256':hashlib.sha256(file.read_bytes()).hexdigest()}) + assert len(set(titles))==len(titles),'duplicate titles' + assert len(set(descriptions))==len(descriptions),'duplicate descriptions' + home=(PUBLIC/'index.html').read_text(encoding='utf-8') + for slug,description in card_copy.items(): + assert description in home,(slug,'homepage card description not visible') + home_schema=Page(home).ld[0]['@graph'] + plans=next(item for item in home_schema if item.get('@id')=='https://servaki.online/#plans') + expected_prices={'Старт':9000,'Бизнес':21000,'Премиум':45000} + actual_prices={offer['name']:int(offer['priceSpecification']['price']) for offer in plans['offers']} + assert actual_prices==expected_prices,('structured tariff prices',actual_prices) + for price in expected_prices.values(): + visible=f'{price:,}'.replace(',', ' ') + assert home.count(visible)==1,('visible tariff price',price) + sample=PUBLIC/'services/networks-vpn/index.html' + broken=sample.read_text(encoding='utf-8').replace('/assets/editorial/photo-networks-vpn-960.webp','/assets/editorial/definitely-missing.webp',1) + try:check(broken,'services/networks-vpn/index.html') + except AssertionError:negative=True + else:raise AssertionError('Negative control did not detect broken image') + locations=[e.text for e in ET.parse(PUBLIC/'sitemap.xml').iter() if e.tag.endswith('}loc')] + assert len(locations)==16 and len(set(locations))==16,'sitemap pages' + small_photos=list((PUBLIC/'assets/editorial').glob('photo-*-480.webp')) + assert len(small_photos)==len(pages) and max(p.stat().st_size for p in small_photos)<50000,'oversized mobile photograph' + report={'html_pages':len(files),'article_pages':12,'animated_equipment_scenes':len(scenes),'max_mobile_photo_bytes':max(p.stat().st_size for p in small_photos),'animation_css_bytes':len(css.encode('utf-8')),'unique_titles':True,'unique_descriptions':True,'tariff_prices':expected_prices,'links_and_fragments':'pass','negative_control':negative,'sitemap_urls':len(locations),'checks':checks} + (ROOT/'verification.json').write_text(json.dumps(report,ensure_ascii=False,indent=2),encoding='utf-8') + print(json.dumps({k:v for k,v in report.items() if k!='checks'},ensure_ascii=False)) +if __name__=='__main__':main() diff --git a/verify_live.py b/verify_live.py new file mode 100644 index 0000000..d96c75b --- /dev/null +++ b/verify_live.py @@ -0,0 +1,64 @@ +"""Check the public Beget release over HTTP and inspect the TLS certificate.""" +from pathlib import Path +from urllib.request import build_opener, ProxyHandler, HTTPSHandler, Request +import json +import socket +import ssl +import sys + +ROOT = Path(__file__).resolve().parent +PUBLIC = ROOT / 'public' +sys.path.insert(0, str(ROOT / '.deps')) +from cryptography import x509 + +BASE = 'https://servaki.online' +opener = build_opener(ProxyHandler({}), HTTPSHandler(context=ssl._create_unverified_context())) + + +def get(path): + request = Request(BASE + path, headers={'Cache-Control': 'no-cache', 'Accept-Encoding': 'identity'}) + with opener.open(request, timeout=20) as response: + return response.status, response.read(), response.url + + +def certificate(): + with socket.create_connection(('servaki.online', 443), timeout=12) as conn: + with ssl._create_unverified_context().wrap_socket(conn, server_hostname='servaki.online') as tls: + cert = x509.load_der_x509_certificate(tls.getpeercert(binary_form=True)) + valid = True + try: + with socket.create_connection(('servaki.online', 443), timeout=12) as conn: + with ssl.create_default_context().wrap_socket(conn, server_hostname='servaki.online'): + pass + except ssl.SSLError: + valid = False + return valid, cert.not_valid_after_utc.isoformat() + + +def main(): + pages = sorted(PUBLIC.rglob('*.html')) + selected = pages + [PUBLIC / 'robots.txt', PUBLIC / 'sitemap.xml'] + selected += [PUBLIC / 'assets/editorial/editorial.css', + PUBLIC / 'assets/editorial/photo-networks-vpn-960.webp', + PUBLIC / 'assets/editorial/networks-vpn-480.webp'] + mismatches = [] + for file in selected: + rel = file.relative_to(PUBLIC).as_posix() + path = '/' if rel == 'index.html' else '/' + rel.removesuffix('index.html') + try: + status, body, final_url = get(path) + if status != 200 or body != file.read_bytes() or not final_url.startswith(BASE + '/'): + mismatches.append({'path': path, 'status': status, 'bytes': len(body)}) + except Exception as error: + mismatches.append({'path': path, 'error': type(error).__name__ + ': ' + str(error)[:130]}) + valid, not_after = certificate() + report = {'checked': len(selected), 'exact_matches': len(selected) - len(mismatches), + 'tls_valid': valid, 'tls_not_after': not_after, 'mismatches': mismatches} + (ROOT / 'live-verification.json').write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding='utf-8') + print(json.dumps(report, ensure_ascii=False)) + if mismatches or not valid: + raise SystemExit(1) + + +if __name__ == '__main__': + main()