Compare commits
8 Commits
925caef547
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| abd626239e | |||
| 4e33aebe55 | |||
| b9d3ac011e | |||
| 73c189ebb5 | |||
| 7f2986489a | |||
| 9f53641f16 | |||
| 21596c278b | |||
| 40250aa4a6 |
85
CLAUDE.md
Normal file
85
CLAUDE.md
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# CLAUDE.md — sag24-website
|
||||||
|
|
||||||
|
Специфика проекта. Общие правила — в `E:\Projects\CLAUDE.md`.
|
||||||
|
|
||||||
|
## Деплой
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Локально: commit + push
|
||||||
|
git add <файлы> && git commit -m "..." && git push
|
||||||
|
|
||||||
|
# 2. На сервере: pull + build
|
||||||
|
ssh -i ~/.ssh/id_ed25519_hhivp striker@str-u-01.striker.su 'cat <<XEOF | sudo -S bash
|
||||||
|
Gh_lpx2018!
|
||||||
|
cd /opt/www/sag24.ru/repo && git pull && npm run deploy
|
||||||
|
XEOF'
|
||||||
|
```
|
||||||
|
|
||||||
|
- `.git` принадлежит root → `git pull` требует sudo
|
||||||
|
- `npm run deploy` = `next build` (→ `../public_html`) + `indexnow.mjs`
|
||||||
|
|
||||||
|
## Singularity
|
||||||
|
|
||||||
|
Проект: **САГ** `P-a41aa057-5401-4ae5-843c-439381fdd7f2`
|
||||||
|
|
||||||
|
## Стек
|
||||||
|
|
||||||
|
- Next.js 15, TypeScript, App Router, static export
|
||||||
|
- Tailwind CSS v3 — конфиг `tailwind.config.js` (НЕ `.ts`!)
|
||||||
|
- i18n через `[lang]` сегмент: `/ru/...`, `/en/...`
|
||||||
|
- Контент: `src/locales/ru.ts`, `src/locales/en.ts`
|
||||||
|
|
||||||
|
## Структура страниц
|
||||||
|
|
||||||
|
```
|
||||||
|
/ru/ → Главная
|
||||||
|
/ru/uslugi/ → Каталог + /[slug]/ (8 услуг)
|
||||||
|
/ru/about/ → О компании
|
||||||
|
/ru/clients/ → Клиенты
|
||||||
|
/ru/partners/ → Партнёры
|
||||||
|
/ru/faq/ → FAQ
|
||||||
|
/ru/kontakty/ → Контакты
|
||||||
|
```
|
||||||
|
|
||||||
|
## SEO
|
||||||
|
|
||||||
|
- BreadcrumbList JSON-LD на всех страницах — `src/lib/breadcrumbs.ts`
|
||||||
|
- generateMetadata на каждой странице с canonical + hreflang
|
||||||
|
- После деплоя IndexNow отправляется автоматически
|
||||||
|
|
||||||
|
## Ограничения
|
||||||
|
|
||||||
|
- Static export — API routes не работают; форма идёт на `/api/contact.php` (PHP)
|
||||||
|
- `trailingSlash: true` — все URL заканчиваются на `/`
|
||||||
|
- Turnstile SITE_KEY: `0x4AAAAAACrQS-dAb7E9RGPQ`
|
||||||
|
|
||||||
|
## Контактная форма
|
||||||
|
|
||||||
|
### Архитектура
|
||||||
|
|
||||||
|
- **Frontend:** `src/app/[lang]/kontakty/page.tsx` — поля: имя*, компания, email, телефон, сообщение* + Turnstile widget
|
||||||
|
- **Backend:** `public/api/contact.php` → деплоится в `public_html/api/contact.php`
|
||||||
|
- **Email:** HTML-формат, `noreply@sag24.ru` → `info@sag24.ru` через mx.hhivp.com:587 (raw fsockopen SMTP)
|
||||||
|
- **Telegram:** бот `vg_contact_bot`, чат `-5230603582`, через CF Worker `https://tg-relay.it-resheniya-2018.workers.dev/bot`
|
||||||
|
- **Защита:** rate limit 5 req/min (temp files), Turnstile обязательный, htmlspecialchars()
|
||||||
|
|
||||||
|
### Секреты (НЕ в git!)
|
||||||
|
|
||||||
|
Файл: `/opt/www/sag24.ru/contact-config.php` (вне вебрута, права 640 www-data)
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
$BOT_TOKEN = '...';
|
||||||
|
$CHAT_ID = '-5230603582';
|
||||||
|
$TURNSTILE_SECRET = '0x4AAAAAACrQSySNBa2C2FWQq2ty1_UyLhc';
|
||||||
|
$smtp_pass = '...';
|
||||||
|
```
|
||||||
|
|
||||||
|
При пересоздании сервера — воссоздать этот файл вручную через SSH.
|
||||||
|
|
||||||
|
### Изменения
|
||||||
|
|
||||||
|
#### 2026-03-21: Секреты в include-файл, HTML email
|
||||||
|
|
||||||
|
- Хардкоженные секреты вынесены в `/opt/www/sag24.ru/contact-config.php`
|
||||||
|
- Email переведён на HTML формат (text/html)
|
||||||
53
README.md
53
README.md
@@ -1,3 +1,54 @@
|
|||||||
# sag24-website
|
# sag24-website
|
||||||
|
|
||||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD> sag24.ru <EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
Сайт sag24.ru — IT-аутсорсинг «Сисадмингрупп» (Пушкино, МО).
|
||||||
|
|
||||||
|
## Стек
|
||||||
|
|
||||||
|
- **Next.js 15** + TypeScript + App Router
|
||||||
|
- **Static export** (`output: 'export'`, `trailingSlash: true`)
|
||||||
|
- **Tailwind CSS v3** — конфиг: `tailwind.config.js` (не .ts!)
|
||||||
|
- **i18n**: `[lang]` сегмент (`/ru/...`, `/en/...`)
|
||||||
|
- **Шрифт**: Manrope (`next/font/google`)
|
||||||
|
|
||||||
|
## Страницы
|
||||||
|
|
||||||
|
```
|
||||||
|
/ru/ → Главная
|
||||||
|
/ru/uslugi/ → Каталог услуг
|
||||||
|
/ru/uslugi/[slug]/ → 8 страниц услуг
|
||||||
|
/ru/about/ → О компании
|
||||||
|
/ru/clients/ → Клиенты
|
||||||
|
/ru/partners/ → Партнёры
|
||||||
|
/ru/faq/ → FAQ
|
||||||
|
/ru/kontakty/ → Контакты
|
||||||
|
/en/... → Аналогично
|
||||||
|
```
|
||||||
|
|
||||||
|
## Деплой
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Локально:
|
||||||
|
git add . && git commit -m "..." && git push
|
||||||
|
|
||||||
|
# На сервере (str-u-01.striker.su):
|
||||||
|
ssh -i ~/.ssh/id_ed25519_hhivp striker@str-u-01.striker.su 'cat <<XEOF | sudo -S bash
|
||||||
|
Gh_lpx2018!
|
||||||
|
cd /opt/www/sag24.ru/repo && git pull && npm run deploy
|
||||||
|
XEOF'
|
||||||
|
```
|
||||||
|
|
||||||
|
`npm run deploy` = `BUILD_DIR=../public_html next build && node scripts/indexnow.mjs`
|
||||||
|
|
||||||
|
После деплоя автоматически отправляется уведомление в IndexNow (Yandex + api.indexnow.org).
|
||||||
|
|
||||||
|
## Локали
|
||||||
|
|
||||||
|
`src/locales/ru.ts` и `en.ts` — весь текстовый контент.
|
||||||
|
|
||||||
|
## SEO
|
||||||
|
|
||||||
|
- GA4: G-C9J0D8FFH3
|
||||||
|
- Yandex.Metrika: 97525679
|
||||||
|
- IndexNow key: 40c65b722891b81d944f2c3fea6cab95
|
||||||
|
- BreadcrumbList JSON-LD на всех страницах (`src/lib/breadcrumbs.ts`)
|
||||||
|
- sitemap.xml: 36 URLs
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"deploy": "BUILD_DIR=../public_html next build"
|
"deploy": "BUILD_DIR=../public_html next build && node scripts/indexnow.mjs"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"next": "^15.2.3",
|
"next": "^15.2.3",
|
||||||
|
|||||||
@@ -4,6 +4,18 @@ header('Access-Control-Allow-Origin: https://sag24.ru');
|
|||||||
header('Access-Control-Allow-Methods: POST');
|
header('Access-Control-Allow-Methods: POST');
|
||||||
header('Access-Control-Allow-Headers: Content-Type');
|
header('Access-Control-Allow-Headers: Content-Type');
|
||||||
|
|
||||||
|
// Load secrets from outside webroot (not in git)
|
||||||
|
$configFile = dirname(__DIR__, 2) . '/contact-config.php';
|
||||||
|
if (file_exists($configFile)) {
|
||||||
|
require_once $configFile;
|
||||||
|
} else {
|
||||||
|
// Fallback values for local development (override via contact-config.php on server)
|
||||||
|
$BOT_TOKEN = getenv('TELEGRAM_BOT_TOKEN') ?: '';
|
||||||
|
$CHAT_ID = getenv('TELEGRAM_CHAT_ID') ?: '';
|
||||||
|
$TURNSTILE_SECRET = getenv('TURNSTILE_SECRET_KEY') ?: '';
|
||||||
|
$smtp_pass = getenv('SMTP_PASS') ?: '';
|
||||||
|
}
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
http_response_code(405);
|
http_response_code(405);
|
||||||
echo json_encode(['error' => 'Method not allowed']);
|
echo json_encode(['error' => 'Method not allowed']);
|
||||||
@@ -62,7 +74,6 @@ if (mb_strlen($phone) > 30) { http_response_code(400); echo json_encode(['error
|
|||||||
if (mb_strlen($message) > 5000) { http_response_code(400); echo json_encode(['error' => 'Message too long']); exit; }
|
if (mb_strlen($message) > 5000) { http_response_code(400); echo json_encode(['error' => 'Message too long']); exit; }
|
||||||
|
|
||||||
// ─── Cloudflare Turnstile verification ───────────────────────────────────────
|
// ─── Cloudflare Turnstile verification ───────────────────────────────────────
|
||||||
$TURNSTILE_SECRET = '0x4AAAAAACrQSySNBa2C2FWQq2ty1_UyLhc';
|
|
||||||
if ($TURNSTILE_SECRET) {
|
if ($TURNSTILE_SECRET) {
|
||||||
if (!$turnstileToken) {
|
if (!$turnstileToken) {
|
||||||
http_response_code(400);
|
http_response_code(400);
|
||||||
@@ -86,8 +97,6 @@ if ($TURNSTILE_SECRET) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ─── Telegram (best-effort, not fatal) ───────────────────────────────────────
|
// ─── Telegram (best-effort, not fatal) ───────────────────────────────────────
|
||||||
$BOT_TOKEN = '8138813013:AAElH2L5NspRLSdiFjDz6Qf32n4G24P_cj8';
|
|
||||||
$CHAT_ID = '-5230603582';
|
|
||||||
|
|
||||||
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? 'unknown';
|
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? 'unknown';
|
||||||
$referer = $_SERVER['HTTP_REFERER'] ?? '';
|
$referer = $_SERVER['HTTP_REFERER'] ?? '';
|
||||||
@@ -120,18 +129,19 @@ curl_close($ch);
|
|||||||
$smtp_host = 'mx.hhivp.com';
|
$smtp_host = 'mx.hhivp.com';
|
||||||
$smtp_port = 587;
|
$smtp_port = 587;
|
||||||
$smtp_user = 'noreply@sag24.ru';
|
$smtp_user = 'noreply@sag24.ru';
|
||||||
$smtp_pass = '9hsnDyBAk5&S4#lE';
|
|
||||||
$mail_to = 'info@sag24.ru';
|
$mail_to = 'info@sag24.ru';
|
||||||
|
|
||||||
$subject = "=?UTF-8?B?" . base64_encode("Заявка с sag24.ru от {$name}") . "?=";
|
$subj_enc = "=?UTF-8?B?" . base64_encode("Заявка с sag24.ru от {$name}") . "?=";
|
||||||
$body = "Имя: {$name}\r\n";
|
$html_body = "<h2>Заявка с sag24.ru</h2>";
|
||||||
if ($company) $body .= "Компания: {$company}\r\n";
|
$html_body .= "<p><strong>Имя:</strong> {$name}</p>";
|
||||||
if ($email) $body .= "Email: {$email}\r\n";
|
if ($company) $html_body .= "<p><strong>Компания:</strong> {$company}</p>";
|
||||||
if ($phone) $body .= "Телефон: {$phone}\r\n";
|
if ($email) $html_body .= "<p><strong>Email:</strong> <a href=\"mailto:{$email}\">{$email}</a></p>";
|
||||||
$body .= "\r\nСообщение:\r\n{$message}\r\n";
|
if ($phone) $html_body .= "<p><strong>Телефон:</strong> {$phone}</p>";
|
||||||
$body .= "\r\nIP: {$ip}";
|
$msg_html = nl2br($message);
|
||||||
if ($country) $body .= " | Страна: {$country}";
|
$html_body .= "<p><strong>Сообщение:</strong><br>{$msg_html}</p>";
|
||||||
$body .= "\r\n" . date('d.m.Y H:i', time() + 3 * 3600) . " MSK";
|
$html_body .= "<hr><p><small>IP: {$ip}";
|
||||||
|
if ($country) $html_body .= " | Страна: {$country}";
|
||||||
|
$html_body .= " | " . date('d.m.Y H:i', time() + 3 * 3600) . " MSK</small></p>";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$smtp = fsockopen($smtp_host, $smtp_port, $errno, $errstr, 10);
|
$smtp = fsockopen($smtp_host, $smtp_port, $errno, $errstr, 10);
|
||||||
@@ -149,9 +159,9 @@ try {
|
|||||||
fwrite($smtp, "MAIL FROM:<{$smtp_user}>\r\n"); fgets($smtp, 512);
|
fwrite($smtp, "MAIL FROM:<{$smtp_user}>\r\n"); fgets($smtp, 512);
|
||||||
fwrite($smtp, "RCPT TO:<{$mail_to}>\r\n"); fgets($smtp, 512);
|
fwrite($smtp, "RCPT TO:<{$mail_to}>\r\n"); fgets($smtp, 512);
|
||||||
fwrite($smtp, "DATA\r\n"); fgets($smtp, 512);
|
fwrite($smtp, "DATA\r\n"); fgets($smtp, 512);
|
||||||
fwrite($smtp, "From: noreply@sag24.ru\r\nTo: {$mail_to}\r\nSubject: {$subject}\r\n");
|
fwrite($smtp, "From: noreply@sag24.ru\r\nTo: {$mail_to}\r\nSubject: {$subj_enc}\r\n");
|
||||||
fwrite($smtp, "MIME-Version: 1.0\r\nContent-Type: text/plain; charset=UTF-8\r\n\r\n");
|
fwrite($smtp, "MIME-Version: 1.0\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n");
|
||||||
fwrite($smtp, $body . "\r\n.\r\n"); fgets($smtp, 512);
|
fwrite($smtp, $html_body . "\r\n.\r\n"); fgets($smtp, 512);
|
||||||
fwrite($smtp, "QUIT\r\n");
|
fwrite($smtp, "QUIT\r\n");
|
||||||
fclose($smtp);
|
fclose($smtp);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,25 @@
|
|||||||
- **IT-аутсорсинг** — полное обслуживание IT-инфраструктуры: серверы, сети, рабочие станции. Поддержка 24/7, удалённое и выездное обслуживание, фиксированная стоимость.
|
- **IT-аутсорсинг** — полное обслуживание IT-инфраструктуры: серверы, сети, рабочие станции. Поддержка 24/7, удалённое и выездное обслуживание, фиксированная стоимость.
|
||||||
- **Кибербезопасность** — защита корпоративных данных, аудит безопасности, настройка межсетевых экранов и VPN, защита от утечек данных.
|
- **Кибербезопасность** — защита корпоративных данных, аудит безопасности, настройка межсетевых экранов и VPN, защита от утечек данных.
|
||||||
- **Техническая поддержка** — help desk, управление заявками, обучение пользователей. SLA от 15 минут.
|
- **Техническая поддержка** — help desk, управление заявками, обучение пользователей. SLA от 15 минут.
|
||||||
|
- **Видеонаблюдение** — проектирование и монтаж систем видеонаблюдения, IP-камеры, NVR, облачное хранение, удалённый доступ.
|
||||||
|
- **Сетевая инфраструктура** — проектирование, монтаж и обслуживание корпоративных сетей, Wi-Fi, VPN, управляемые коммутаторы.
|
||||||
|
- **Серверная инфраструктура** — поставка, настройка и обслуживание серверов, виртуализация, резервное копирование.
|
||||||
|
- **IP-телефония** — корпоративные АТС, SIP-телефония, интеграция с CRM, запись звонков.
|
||||||
|
- **Облачные решения** — перенос инфраструктуры в облако, Microsoft 365, резервное копирование в облаке.
|
||||||
|
|
||||||
|
## Страницы
|
||||||
|
|
||||||
|
- Главная: https://sag24.ru/ru/
|
||||||
|
- Услуги: https://sag24.ru/ru/uslugi/
|
||||||
|
- О компании: https://sag24.ru/ru/about/
|
||||||
|
- Клиенты: https://sag24.ru/ru/clients/
|
||||||
|
- Партнёры: https://sag24.ru/ru/partners/
|
||||||
|
- FAQ: https://sag24.ru/ru/faq/
|
||||||
|
- Контакты: https://sag24.ru/ru/kontakty/
|
||||||
|
|
||||||
## Контакты
|
## Контакты
|
||||||
|
|
||||||
- Телефон: +7 495 363-74-76
|
- Телефон: +7 495 363-74-76, +7 495 363-73-35, +7 909 945-44-56
|
||||||
- Email: info@sag24.ru
|
- Email: info@sag24.ru
|
||||||
- Адрес: Пушкино, Московская область
|
- Адрес: Пушкино, пр-кт Московский, д. 38/14, Московская область, 141207
|
||||||
- Сайт: https://sag24.ru
|
- Сайт: https://sag24.ru
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
<loc>https://sag24.ru/ru/</loc>
|
<loc>https://sag24.ru/ru/</loc>
|
||||||
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/"/>
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/"/>
|
||||||
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/"/>
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/"/>
|
||||||
<lastmod>2026-03-15</lastmod>
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
<changefreq>monthly</changefreq>
|
<changefreq>monthly</changefreq>
|
||||||
<priority>1.0</priority>
|
<priority>1.0</priority>
|
||||||
</url>
|
</url>
|
||||||
@@ -15,27 +16,99 @@
|
|||||||
<loc>https://sag24.ru/en/</loc>
|
<loc>https://sag24.ru/en/</loc>
|
||||||
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/"/>
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/"/>
|
||||||
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/"/>
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/"/>
|
||||||
<lastmod>2026-03-15</lastmod>
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
<changefreq>monthly</changefreq>
|
<changefreq>monthly</changefreq>
|
||||||
<priority>0.8</priority>
|
<priority>0.8</priority>
|
||||||
</url>
|
</url>
|
||||||
|
|
||||||
|
<!-- О компании -->
|
||||||
|
<url>
|
||||||
|
<loc>https://sag24.ru/ru/about/</loc>
|
||||||
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/about/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/about/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/about/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.8</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://sag24.ru/en/about/</loc>
|
||||||
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/about/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/about/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/about/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.7</priority>
|
||||||
|
</url>
|
||||||
|
|
||||||
|
<!-- Клиенты -->
|
||||||
|
<url>
|
||||||
|
<loc>https://sag24.ru/ru/clients/</loc>
|
||||||
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/clients/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/clients/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/clients/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.7</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://sag24.ru/en/clients/</loc>
|
||||||
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/clients/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/clients/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/clients/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.6</priority>
|
||||||
|
</url>
|
||||||
|
|
||||||
|
<!-- Партнёры -->
|
||||||
|
<url>
|
||||||
|
<loc>https://sag24.ru/ru/partners/</loc>
|
||||||
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/partners/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/partners/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/partners/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.7</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://sag24.ru/en/partners/</loc>
|
||||||
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/partners/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/partners/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/partners/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.6</priority>
|
||||||
|
</url>
|
||||||
|
|
||||||
<!-- Каталог услуг -->
|
<!-- Каталог услуг -->
|
||||||
<url>
|
<url>
|
||||||
<loc>https://sag24.ru/ru/uslugi/</loc>
|
<loc>https://sag24.ru/ru/uslugi/</loc>
|
||||||
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/uslugi/"/>
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/uslugi/"/>
|
||||||
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/uslugi/"/>
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/uslugi/"/>
|
||||||
<lastmod>2026-03-15</lastmod>
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/uslugi/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
<changefreq>monthly</changefreq>
|
<changefreq>monthly</changefreq>
|
||||||
<priority>0.9</priority>
|
<priority>0.9</priority>
|
||||||
</url>
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://sag24.ru/en/uslugi/</loc>
|
||||||
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/uslugi/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/uslugi/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/uslugi/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.7</priority>
|
||||||
|
</url>
|
||||||
|
|
||||||
<!-- Услуги -->
|
<!-- Услуги RU -->
|
||||||
<url>
|
<url>
|
||||||
<loc>https://sag24.ru/ru/uslugi/it-autsorsing/</loc>
|
<loc>https://sag24.ru/ru/uslugi/it-autsorsing/</loc>
|
||||||
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/uslugi/it-autsorsing/"/>
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/uslugi/it-autsorsing/"/>
|
||||||
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/uslugi/it-autsorsing/"/>
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/uslugi/it-autsorsing/"/>
|
||||||
<lastmod>2026-03-15</lastmod>
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/uslugi/it-autsorsing/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
<changefreq>monthly</changefreq>
|
<changefreq>monthly</changefreq>
|
||||||
<priority>0.8</priority>
|
<priority>0.8</priority>
|
||||||
</url>
|
</url>
|
||||||
@@ -43,7 +116,8 @@
|
|||||||
<loc>https://sag24.ru/ru/uslugi/kiberbezopasnost/</loc>
|
<loc>https://sag24.ru/ru/uslugi/kiberbezopasnost/</loc>
|
||||||
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/uslugi/kiberbezopasnost/"/>
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/uslugi/kiberbezopasnost/"/>
|
||||||
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/uslugi/kiberbezopasnost/"/>
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/uslugi/kiberbezopasnost/"/>
|
||||||
<lastmod>2026-03-15</lastmod>
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/uslugi/kiberbezopasnost/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
<changefreq>monthly</changefreq>
|
<changefreq>monthly</changefreq>
|
||||||
<priority>0.8</priority>
|
<priority>0.8</priority>
|
||||||
</url>
|
</url>
|
||||||
@@ -51,7 +125,8 @@
|
|||||||
<loc>https://sag24.ru/ru/uslugi/tehpodderzhka/</loc>
|
<loc>https://sag24.ru/ru/uslugi/tehpodderzhka/</loc>
|
||||||
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/uslugi/tehpodderzhka/"/>
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/uslugi/tehpodderzhka/"/>
|
||||||
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/uslugi/tehpodderzhka/"/>
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/uslugi/tehpodderzhka/"/>
|
||||||
<lastmod>2026-03-15</lastmod>
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/uslugi/tehpodderzhka/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
<changefreq>monthly</changefreq>
|
<changefreq>monthly</changefreq>
|
||||||
<priority>0.8</priority>
|
<priority>0.8</priority>
|
||||||
</url>
|
</url>
|
||||||
@@ -59,7 +134,8 @@
|
|||||||
<loc>https://sag24.ru/ru/uslugi/videonablyudenie/</loc>
|
<loc>https://sag24.ru/ru/uslugi/videonablyudenie/</loc>
|
||||||
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/uslugi/videonablyudenie/"/>
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/uslugi/videonablyudenie/"/>
|
||||||
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/uslugi/videonablyudenie/"/>
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/uslugi/videonablyudenie/"/>
|
||||||
<lastmod>2026-03-15</lastmod>
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/uslugi/videonablyudenie/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
<changefreq>monthly</changefreq>
|
<changefreq>monthly</changefreq>
|
||||||
<priority>0.8</priority>
|
<priority>0.8</priority>
|
||||||
</url>
|
</url>
|
||||||
@@ -67,7 +143,8 @@
|
|||||||
<loc>https://sag24.ru/ru/uslugi/seti/</loc>
|
<loc>https://sag24.ru/ru/uslugi/seti/</loc>
|
||||||
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/uslugi/seti/"/>
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/uslugi/seti/"/>
|
||||||
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/uslugi/seti/"/>
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/uslugi/seti/"/>
|
||||||
<lastmod>2026-03-15</lastmod>
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/uslugi/seti/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
<changefreq>monthly</changefreq>
|
<changefreq>monthly</changefreq>
|
||||||
<priority>0.8</priority>
|
<priority>0.8</priority>
|
||||||
</url>
|
</url>
|
||||||
@@ -75,7 +152,8 @@
|
|||||||
<loc>https://sag24.ru/ru/uslugi/servery/</loc>
|
<loc>https://sag24.ru/ru/uslugi/servery/</loc>
|
||||||
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/uslugi/servery/"/>
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/uslugi/servery/"/>
|
||||||
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/uslugi/servery/"/>
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/uslugi/servery/"/>
|
||||||
<lastmod>2026-03-15</lastmod>
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/uslugi/servery/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
<changefreq>monthly</changefreq>
|
<changefreq>monthly</changefreq>
|
||||||
<priority>0.8</priority>
|
<priority>0.8</priority>
|
||||||
</url>
|
</url>
|
||||||
@@ -83,7 +161,8 @@
|
|||||||
<loc>https://sag24.ru/ru/uslugi/telefoniya/</loc>
|
<loc>https://sag24.ru/ru/uslugi/telefoniya/</loc>
|
||||||
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/uslugi/telefoniya/"/>
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/uslugi/telefoniya/"/>
|
||||||
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/uslugi/telefoniya/"/>
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/uslugi/telefoniya/"/>
|
||||||
<lastmod>2026-03-15</lastmod>
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/uslugi/telefoniya/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
<changefreq>monthly</changefreq>
|
<changefreq>monthly</changefreq>
|
||||||
<priority>0.8</priority>
|
<priority>0.8</priority>
|
||||||
</url>
|
</url>
|
||||||
@@ -91,29 +170,124 @@
|
|||||||
<loc>https://sag24.ru/ru/uslugi/oblako/</loc>
|
<loc>https://sag24.ru/ru/uslugi/oblako/</loc>
|
||||||
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/uslugi/oblako/"/>
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/uslugi/oblako/"/>
|
||||||
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/uslugi/oblako/"/>
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/uslugi/oblako/"/>
|
||||||
<lastmod>2026-03-15</lastmod>
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/uslugi/oblako/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
<changefreq>monthly</changefreq>
|
<changefreq>monthly</changefreq>
|
||||||
<priority>0.8</priority>
|
<priority>0.8</priority>
|
||||||
</url>
|
</url>
|
||||||
|
|
||||||
|
<!-- Услуги EN -->
|
||||||
|
<url>
|
||||||
|
<loc>https://sag24.ru/en/uslugi/it-autsorsing/</loc>
|
||||||
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/uslugi/it-autsorsing/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/uslugi/it-autsorsing/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/uslugi/it-autsorsing/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.6</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://sag24.ru/en/uslugi/kiberbezopasnost/</loc>
|
||||||
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/uslugi/kiberbezopasnost/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/uslugi/kiberbezopasnost/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/uslugi/kiberbezopasnost/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.6</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://sag24.ru/en/uslugi/tehpodderzhka/</loc>
|
||||||
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/uslugi/tehpodderzhka/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/uslugi/tehpodderzhka/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/uslugi/tehpodderzhka/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.6</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://sag24.ru/en/uslugi/videonablyudenie/</loc>
|
||||||
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/uslugi/videonablyudenie/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/uslugi/videonablyudenie/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/uslugi/videonablyudenie/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.6</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://sag24.ru/en/uslugi/seti/</loc>
|
||||||
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/uslugi/seti/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/uslugi/seti/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/uslugi/seti/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.6</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://sag24.ru/en/uslugi/servery/</loc>
|
||||||
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/uslugi/servery/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/uslugi/servery/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/uslugi/servery/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.6</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://sag24.ru/en/uslugi/telefoniya/</loc>
|
||||||
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/uslugi/telefoniya/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/uslugi/telefoniya/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/uslugi/telefoniya/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.6</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://sag24.ru/en/uslugi/oblako/</loc>
|
||||||
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/uslugi/oblako/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/uslugi/oblako/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/uslugi/oblako/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.6</priority>
|
||||||
|
</url>
|
||||||
|
|
||||||
<!-- FAQ -->
|
<!-- FAQ -->
|
||||||
<url>
|
<url>
|
||||||
<loc>https://sag24.ru/ru/faq/</loc>
|
<loc>https://sag24.ru/ru/faq/</loc>
|
||||||
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/faq/"/>
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/faq/"/>
|
||||||
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/faq/"/>
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/faq/"/>
|
||||||
<lastmod>2026-03-15</lastmod>
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/faq/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
<changefreq>monthly</changefreq>
|
<changefreq>monthly</changefreq>
|
||||||
<priority>0.7</priority>
|
<priority>0.7</priority>
|
||||||
</url>
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://sag24.ru/en/faq/</loc>
|
||||||
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/faq/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/faq/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/faq/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.6</priority>
|
||||||
|
</url>
|
||||||
|
|
||||||
<!-- Контакты -->
|
<!-- Контакты -->
|
||||||
<url>
|
<url>
|
||||||
<loc>https://sag24.ru/ru/kontakty/</loc>
|
<loc>https://sag24.ru/ru/kontakty/</loc>
|
||||||
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/kontakty/"/>
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/kontakty/"/>
|
||||||
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/kontakty/"/>
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/kontakty/"/>
|
||||||
<lastmod>2026-03-15</lastmod>
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/kontakty/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
<changefreq>monthly</changefreq>
|
<changefreq>monthly</changefreq>
|
||||||
<priority>0.7</priority>
|
<priority>0.7</priority>
|
||||||
</url>
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://sag24.ru/en/kontakty/</loc>
|
||||||
|
<xhtml:link rel="alternate" hreflang="ru" href="https://sag24.ru/ru/kontakty/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="en" href="https://sag24.ru/en/kontakty/"/>
|
||||||
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://sag24.ru/ru/kontakty/"/>
|
||||||
|
<lastmod>2026-03-16</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.6</priority>
|
||||||
|
</url>
|
||||||
|
|
||||||
</urlset>
|
</urlset>
|
||||||
|
|||||||
68
scripts/indexnow.mjs
Normal file
68
scripts/indexnow.mjs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
// IndexNow — уведомляем поисковики об обновлении сайта после деплоя
|
||||||
|
// https://www.indexnow.org/
|
||||||
|
|
||||||
|
const KEY = '40c65b722891b81d944f2c3fea6cab95'
|
||||||
|
const HOST = 'sag24.ru'
|
||||||
|
|
||||||
|
const URLS = [
|
||||||
|
'https://sag24.ru/ru/',
|
||||||
|
'https://sag24.ru/en/',
|
||||||
|
'https://sag24.ru/ru/about/',
|
||||||
|
'https://sag24.ru/en/about/',
|
||||||
|
'https://sag24.ru/ru/clients/',
|
||||||
|
'https://sag24.ru/en/clients/',
|
||||||
|
'https://sag24.ru/ru/partners/',
|
||||||
|
'https://sag24.ru/en/partners/',
|
||||||
|
'https://sag24.ru/ru/uslugi/',
|
||||||
|
'https://sag24.ru/en/uslugi/',
|
||||||
|
'https://sag24.ru/ru/uslugi/it-autsorsing/',
|
||||||
|
'https://sag24.ru/en/uslugi/it-autsorsing/',
|
||||||
|
'https://sag24.ru/ru/uslugi/kiberbezopasnost/',
|
||||||
|
'https://sag24.ru/en/uslugi/kiberbezopasnost/',
|
||||||
|
'https://sag24.ru/ru/uslugi/tehpodderzhka/',
|
||||||
|
'https://sag24.ru/en/uslugi/tehpodderzhka/',
|
||||||
|
'https://sag24.ru/ru/uslugi/videonablyudenie/',
|
||||||
|
'https://sag24.ru/en/uslugi/videonablyudenie/',
|
||||||
|
'https://sag24.ru/ru/uslugi/seti/',
|
||||||
|
'https://sag24.ru/en/uslugi/seti/',
|
||||||
|
'https://sag24.ru/ru/uslugi/servery/',
|
||||||
|
'https://sag24.ru/en/uslugi/servery/',
|
||||||
|
'https://sag24.ru/ru/uslugi/telefoniya/',
|
||||||
|
'https://sag24.ru/en/uslugi/telefoniya/',
|
||||||
|
'https://sag24.ru/ru/uslugi/oblako/',
|
||||||
|
'https://sag24.ru/en/uslugi/oblako/',
|
||||||
|
'https://sag24.ru/ru/faq/',
|
||||||
|
'https://sag24.ru/en/faq/',
|
||||||
|
'https://sag24.ru/ru/kontakty/',
|
||||||
|
'https://sag24.ru/en/kontakty/',
|
||||||
|
]
|
||||||
|
|
||||||
|
const ENDPOINTS = [
|
||||||
|
'https://api.indexnow.org/indexnow',
|
||||||
|
'https://yandex.com/indexnow',
|
||||||
|
]
|
||||||
|
|
||||||
|
async function submit(endpoint) {
|
||||||
|
const res = await fetch(endpoint, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json; charset=utf-8' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
host: HOST,
|
||||||
|
key: KEY,
|
||||||
|
keyLocation: `https://${HOST}/${KEY}.txt`,
|
||||||
|
urlList: URLS,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
return res.status
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`IndexNow: submitting ${URLS.length} URLs...`)
|
||||||
|
const results = await Promise.allSettled(ENDPOINTS.map(async ep => {
|
||||||
|
const status = await submit(ep)
|
||||||
|
console.log(` ${ep} → ${status}`)
|
||||||
|
return status
|
||||||
|
}))
|
||||||
|
|
||||||
|
const ok = results.every(r => r.status === 'fulfilled' && (r.value === 200 || r.value === 202))
|
||||||
|
console.log(ok ? 'IndexNow: done.' : 'IndexNow: some errors (non-critical).')
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
import type { Metadata } from 'next'
|
import type { Metadata } from 'next'
|
||||||
import { getDictionary, LOCALES, type Locale } from '@/lib/i18n'
|
import { getDictionary, LOCALES, type Locale } from '@/lib/i18n'
|
||||||
|
import { breadcrumbSchema } from '@/lib/breadcrumbs'
|
||||||
import AboutSection from '@/components/sections/AboutSection'
|
import AboutSection from '@/components/sections/AboutSection'
|
||||||
import ClientsSection from '@/components/sections/ClientsSection'
|
|
||||||
import PartnersSection from '@/components/sections/PartnersSection'
|
|
||||||
|
|
||||||
export function generateStaticParams() {
|
export function generateStaticParams() {
|
||||||
return LOCALES.map(lang => ({ lang }))
|
return LOCALES.map(lang => ({ lang }))
|
||||||
@@ -14,22 +13,22 @@ export async function generateMetadata({ params }: { params: Promise<{ lang: str
|
|||||||
return {
|
return {
|
||||||
title: isRu ? 'О компании — Сисадмингрупп' : 'About Us — SysadminGroup',
|
title: isRu ? 'О компании — Сисадмингрупп' : 'About Us — SysadminGroup',
|
||||||
description: isRu
|
description: isRu
|
||||||
? 'Сисадмингрупп — IT-аутсорсинг в Пушкино и Московской области. Более 10 лет опыта, 150+ клиентов, надёжные партнёры.'
|
? 'Сисадмингрупп — IT-аутсорсинг в Пушкино и Московской области. Более 10 лет опыта, 150+ клиентов.'
|
||||||
: 'SysadminGroup — IT outsourcing in Pushkino and Moscow Region. Over 10 years experience, 150+ clients, trusted partners.',
|
: 'SysadminGroup — IT outsourcing in Pushkino and Moscow Region. Over 10 years experience, 150+ clients.',
|
||||||
alternates: {
|
alternates: {
|
||||||
canonical: `https://sag24.ru/${lang}/o-kompanii/`,
|
canonical: `https://sag24.ru/${lang}/about/`,
|
||||||
languages: {
|
languages: {
|
||||||
'ru': 'https://sag24.ru/ru/o-kompanii/',
|
'ru': 'https://sag24.ru/ru/about/',
|
||||||
'en': 'https://sag24.ru/en/o-kompanii/',
|
'en': 'https://sag24.ru/en/about/',
|
||||||
'x-default': 'https://sag24.ru/ru/o-kompanii/',
|
'x-default': 'https://sag24.ru/ru/about/',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: isRu ? 'О компании — Сисадмингрупп' : 'About Us — SysadminGroup',
|
title: isRu ? 'О компании — Сисадмингрупп' : 'About Us — SysadminGroup',
|
||||||
description: isRu
|
description: isRu
|
||||||
? 'Сисадмингрупп — IT-аутсорсинг в Пушкино и Московской области. Более 10 лет опыта, 150+ клиентов.'
|
? 'Сисадмингрупп — IT-аутсорсинг в Пушкино и Московской области. 10+ лет опыта, 150+ клиентов.'
|
||||||
: 'SysadminGroup — IT outsourcing in Pushkino and Moscow Region. Over 10 years experience, 150+ clients.',
|
: 'SysadminGroup — IT outsourcing in Pushkino and Moscow Region. 10+ years experience, 150+ clients.',
|
||||||
url: `https://sag24.ru/${lang}/o-kompanii/`,
|
url: `https://sag24.ru/${lang}/about/`,
|
||||||
images: [{ url: '/og-image.png' }],
|
images: [{ url: '/og-image.png' }],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -39,11 +38,14 @@ export default async function AboutPage({ params }: { params: Promise<{ lang: st
|
|||||||
const { lang: langStr } = await params
|
const { lang: langStr } = await params
|
||||||
const lang = langStr as Locale
|
const lang = langStr as Locale
|
||||||
const d = getDictionary(lang)
|
const d = getDictionary(lang)
|
||||||
|
const isRu = lang === 'ru'
|
||||||
return (
|
return (
|
||||||
<div className="pt-16">
|
<div className="pt-16">
|
||||||
<AboutSection d={d} standalone />
|
<AboutSection d={d} standalone />
|
||||||
<ClientsSection d={d} />
|
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbSchema([
|
||||||
<PartnersSection d={d} />
|
{ name: isRu ? 'Главная' : 'Home', url: `https://sag24.ru/${lang}/` },
|
||||||
|
{ name: isRu ? 'О компании' : 'About', url: `https://sag24.ru/${lang}/about/` },
|
||||||
|
])) }} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
51
src/app/[lang]/clients/page.tsx
Normal file
51
src/app/[lang]/clients/page.tsx
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import type { Metadata } from 'next'
|
||||||
|
import { getDictionary, LOCALES, type Locale } from '@/lib/i18n'
|
||||||
|
import { breadcrumbSchema } from '@/lib/breadcrumbs'
|
||||||
|
import ClientsSection from '@/components/sections/ClientsSection'
|
||||||
|
|
||||||
|
export function generateStaticParams() {
|
||||||
|
return LOCALES.map(lang => ({ lang }))
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function generateMetadata({ params }: { params: Promise<{ lang: string }> }): Promise<Metadata> {
|
||||||
|
const { lang } = await params
|
||||||
|
const isRu = lang === 'ru'
|
||||||
|
return {
|
||||||
|
title: isRu ? 'Наши клиенты — Сисадмингрупп' : 'Our Clients — SysadminGroup',
|
||||||
|
description: isRu
|
||||||
|
? 'Компании, которые доверяют IT-инфраструктуру Сисадмингрупп. 150+ клиентов в Пушкино и Московской области.'
|
||||||
|
: 'Companies that trust SysadminGroup with their IT infrastructure. 150+ clients in Moscow Region.',
|
||||||
|
alternates: {
|
||||||
|
canonical: `https://sag24.ru/${lang}/clients/`,
|
||||||
|
languages: {
|
||||||
|
'ru': 'https://sag24.ru/ru/clients/',
|
||||||
|
'en': 'https://sag24.ru/en/clients/',
|
||||||
|
'x-default': 'https://sag24.ru/ru/clients/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
openGraph: {
|
||||||
|
title: isRu ? 'Наши клиенты — Сисадмингрупп' : 'Our Clients — SysadminGroup',
|
||||||
|
description: isRu
|
||||||
|
? '150+ компаний доверяют нам своё IT в Пушкино и Московской области.'
|
||||||
|
: '150+ companies trust us with their IT in Moscow Region.',
|
||||||
|
url: `https://sag24.ru/${lang}/clients/`,
|
||||||
|
images: [{ url: '/og-image.png' }],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function ClientsPage({ params }: { params: Promise<{ lang: string }> }) {
|
||||||
|
const { lang: langStr } = await params
|
||||||
|
const lang = langStr as Locale
|
||||||
|
const d = getDictionary(lang)
|
||||||
|
const isRu = lang === 'ru'
|
||||||
|
return (
|
||||||
|
<div className="pt-16">
|
||||||
|
<ClientsSection d={d} standalone />
|
||||||
|
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbSchema([
|
||||||
|
{ name: isRu ? 'Главная' : 'Home', url: `https://sag24.ru/${lang}/` },
|
||||||
|
{ name: isRu ? 'Клиенты' : 'Clients', url: `https://sag24.ru/${lang}/clients/` },
|
||||||
|
])) }} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { Metadata } from 'next'
|
import type { Metadata } from 'next'
|
||||||
import { getDictionary, LOCALES, type Locale } from '@/lib/i18n'
|
import { getDictionary, LOCALES, type Locale } from '@/lib/i18n'
|
||||||
|
import { breadcrumbSchema } from '@/lib/breadcrumbs'
|
||||||
import FaqSection from '@/components/sections/FaqSection'
|
import FaqSection from '@/components/sections/FaqSection'
|
||||||
|
|
||||||
export function generateStaticParams() {
|
export function generateStaticParams() {
|
||||||
@@ -49,13 +50,15 @@ export default async function FaqPage({ params }: { params: Promise<{ lang: stri
|
|||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isRu = lang === 'ru'
|
||||||
return (
|
return (
|
||||||
<div className="pt-16">
|
<div className="pt-16">
|
||||||
<FaqSection d={d} standalone />
|
<FaqSection d={d} standalone />
|
||||||
<script
|
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(faqSchema) }} />
|
||||||
type="application/ld+json"
|
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbSchema([
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(faqSchema) }}
|
{ name: isRu ? 'Главная' : 'Home', url: `https://sag24.ru/${lang}/` },
|
||||||
/>
|
{ name: 'FAQ', url: `https://sag24.ru/${lang}/faq/` },
|
||||||
|
])) }} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { Metadata } from 'next'
|
import type { Metadata } from 'next'
|
||||||
import { getDictionary, LOCALES, type Locale } from '@/lib/i18n'
|
import { getDictionary, LOCALES, type Locale } from '@/lib/i18n'
|
||||||
|
import { breadcrumbSchema } from '@/lib/breadcrumbs'
|
||||||
import ContactForm from '@/components/ui/ContactForm'
|
import ContactForm from '@/components/ui/ContactForm'
|
||||||
import { Phone, Mail, MapPin } from 'lucide-react'
|
import { Phone, Mail, MapPin } from 'lucide-react'
|
||||||
|
|
||||||
@@ -128,10 +129,11 @@ export default async function KontaktyPage({ params }: { params: Promise<{ lang:
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<script
|
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(localBusinessSchema) }} />
|
||||||
type="application/ld+json"
|
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbSchema([
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(localBusinessSchema) }}
|
{ name: lang === 'ru' ? 'Главная' : 'Home', url: `https://sag24.ru/${lang}/` },
|
||||||
/>
|
{ name: lang === 'ru' ? 'Контакты' : 'Contact', url: `https://sag24.ru/${lang}/kontakty/` },
|
||||||
|
])) }} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,12 +23,12 @@ export default async function LangLayout({
|
|||||||
}) {
|
}) {
|
||||||
const { lang } = await params
|
const { lang } = await params
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="flex flex-col min-h-screen">
|
||||||
<Header lang={lang} />
|
<Header lang={lang} />
|
||||||
<main>{children}</main>
|
<main className="flex-1">{children}</main>
|
||||||
<Footer lang={lang} />
|
<Footer lang={lang} />
|
||||||
<FloatingContacts />
|
<FloatingContacts />
|
||||||
<ScrollToTop />
|
<ScrollToTop />
|
||||||
</>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
51
src/app/[lang]/partners/page.tsx
Normal file
51
src/app/[lang]/partners/page.tsx
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import type { Metadata } from 'next'
|
||||||
|
import { getDictionary, LOCALES, type Locale } from '@/lib/i18n'
|
||||||
|
import { breadcrumbSchema } from '@/lib/breadcrumbs'
|
||||||
|
import PartnersSection from '@/components/sections/PartnersSection'
|
||||||
|
|
||||||
|
export function generateStaticParams() {
|
||||||
|
return LOCALES.map(lang => ({ lang }))
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function generateMetadata({ params }: { params: Promise<{ lang: string }> }): Promise<Metadata> {
|
||||||
|
const { lang } = await params
|
||||||
|
const isRu = lang === 'ru'
|
||||||
|
return {
|
||||||
|
title: isRu ? 'Партнёры — Сисадмингрупп' : 'Partners — SysadminGroup',
|
||||||
|
description: isRu
|
||||||
|
? 'Технологические партнёры Сисадмингрупп: RU-CENTER, REG.RU, МТВ, КОНТУР. Официальные представители.'
|
||||||
|
: 'Technology partners of SysadminGroup: RU-CENTER, REG.RU, MTV, Kontur. Authorized representatives.',
|
||||||
|
alternates: {
|
||||||
|
canonical: `https://sag24.ru/${lang}/partners/`,
|
||||||
|
languages: {
|
||||||
|
'ru': 'https://sag24.ru/ru/partners/',
|
||||||
|
'en': 'https://sag24.ru/en/partners/',
|
||||||
|
'x-default': 'https://sag24.ru/ru/partners/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
openGraph: {
|
||||||
|
title: isRu ? 'Партнёры — Сисадмингрупп' : 'Partners — SysadminGroup',
|
||||||
|
description: isRu
|
||||||
|
? 'Технологические партнёры: RU-CENTER, REG.RU, МТВ, КОНТУР.'
|
||||||
|
: 'Technology partners: RU-CENTER, REG.RU, MTV, Kontur.',
|
||||||
|
url: `https://sag24.ru/${lang}/partners/`,
|
||||||
|
images: [{ url: '/og-image.png' }],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function PartnersPage({ params }: { params: Promise<{ lang: string }> }) {
|
||||||
|
const { lang: langStr } = await params
|
||||||
|
const lang = langStr as Locale
|
||||||
|
const d = getDictionary(lang)
|
||||||
|
const isRu = lang === 'ru'
|
||||||
|
return (
|
||||||
|
<div className="pt-16">
|
||||||
|
<PartnersSection d={d} standalone />
|
||||||
|
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbSchema([
|
||||||
|
{ name: isRu ? 'Главная' : 'Home', url: `https://sag24.ru/${lang}/` },
|
||||||
|
{ name: isRu ? 'Партнёры' : 'Partners', url: `https://sag24.ru/${lang}/partners/` },
|
||||||
|
])) }} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import Link from 'next/link'
|
|||||||
import { notFound } from 'next/navigation'
|
import { notFound } from 'next/navigation'
|
||||||
import { getDictionary, LOCALES, type Locale } from '@/lib/i18n'
|
import { getDictionary, LOCALES, type Locale } from '@/lib/i18n'
|
||||||
import { SERVICE_SLUGS } from '@/lib/services'
|
import { SERVICE_SLUGS } from '@/lib/services'
|
||||||
|
import { breadcrumbSchema } from '@/lib/breadcrumbs'
|
||||||
import { Server, Shield, Headphones, Camera, Network, HardDrive, Phone, Cloud, CheckCircle2, ChevronRight } from 'lucide-react'
|
import { Server, Shield, Headphones, Camera, Network, HardDrive, Phone, Cloud, CheckCircle2, ChevronRight } from 'lucide-react'
|
||||||
|
|
||||||
const ICONS = [Server, Shield, Headphones, Camera, Network, HardDrive, Phone, Cloud]
|
const ICONS = [Server, Shield, Headphones, Camera, Network, HardDrive, Phone, Cloud]
|
||||||
@@ -109,6 +110,14 @@ export default async function ServicePage({ params }: { params: Promise<{ lang:
|
|||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script
|
||||||
|
type="application/ld+json"
|
||||||
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbSchema([
|
||||||
|
{ name: lang === 'ru' ? 'Главная' : 'Home', url: `https://sag24.ru/${lang}/` },
|
||||||
|
{ name: lang === 'ru' ? 'Услуги' : 'Services', url: `https://sag24.ru/${lang}/uslugi/` },
|
||||||
|
{ name: svc.title, url: `https://sag24.ru/${lang}/uslugi/${slug}/` },
|
||||||
|
])) }}
|
||||||
|
/>
|
||||||
<script
|
<script
|
||||||
type="application/ld+json"
|
type="application/ld+json"
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { Metadata } from 'next'
|
import type { Metadata } from 'next'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { getDictionary, LOCALES, type Locale } from '@/lib/i18n'
|
import { getDictionary, LOCALES, type Locale } from '@/lib/i18n'
|
||||||
|
import { breadcrumbSchema } from '@/lib/breadcrumbs'
|
||||||
import { Server, Shield, Headphones, Camera, Network, HardDrive, Phone, Cloud } from 'lucide-react'
|
import { Server, Shield, Headphones, Camera, Network, HardDrive, Phone, Cloud } from 'lucide-react'
|
||||||
|
|
||||||
export function generateStaticParams() {
|
export function generateStaticParams() {
|
||||||
@@ -78,6 +79,10 @@ export default async function UslugiPage({ params }: { params: Promise<{ lang: s
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbSchema([
|
||||||
|
{ name: lang === 'ru' ? 'Главная' : 'Home', url: `https://sag24.ru/${lang}/` },
|
||||||
|
{ name: lang === 'ru' ? 'Услуги' : 'Services', url: `https://sag24.ru/${lang}/uslugi/` },
|
||||||
|
])) }} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,14 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
|||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||||
<Script src="https://challenges.cloudflare.com/turnstile/v0/api.js" strategy="lazyOnload" />
|
<Script src="https://challenges.cloudflare.com/turnstile/v0/api.js" strategy="lazyOnload" />
|
||||||
|
{/* Google Analytics GA4 */}
|
||||||
|
<Script src="https://www.googletagmanager.com/gtag/js?id=G-C9J0D8FFH3" strategy="afterInteractive" />
|
||||||
|
<Script id="ga4" strategy="afterInteractive">{`
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag(){dataLayer.push(arguments);}
|
||||||
|
gtag('js', new Date());
|
||||||
|
gtag('config', 'G-C9J0D8FFH3');
|
||||||
|
`}</Script>
|
||||||
{/* Yandex.Metrika */}
|
{/* Yandex.Metrika */}
|
||||||
<Script id="ym" strategy="afterInteractive">{`
|
<Script id="ym" strategy="afterInteractive">{`
|
||||||
(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
|
(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
|
||||||
|
|||||||
@@ -1,32 +1,82 @@
|
|||||||
import type { Metadata } from 'next'
|
'use client'
|
||||||
import Link from 'next/link'
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
import Link from 'next/link'
|
||||||
title: 'Страница не найдена — Сисадмингрупп',
|
import { usePathname } from 'next/navigation'
|
||||||
description: 'Запрошенная страница не существует.',
|
import { Home, Wrench, Mail, ArrowLeft } from 'lucide-react'
|
||||||
robots: { index: false },
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function NotFound() {
|
export default function NotFound() {
|
||||||
|
const pathname = usePathname()
|
||||||
|
const lang = pathname?.startsWith('/en') ? 'en' : 'ru'
|
||||||
|
|
||||||
|
const t = lang === 'en' ? {
|
||||||
|
subtitle: 'Page Not Found',
|
||||||
|
description: 'The page you are looking for does not exist or has been moved.',
|
||||||
|
home: 'Go Home',
|
||||||
|
services: 'Our Services',
|
||||||
|
contact: 'Contact Us',
|
||||||
|
back: 'Go Back',
|
||||||
|
} : {
|
||||||
|
subtitle: 'Страница не найдена',
|
||||||
|
description: 'Запрошенная страница не существует или была перемещена.',
|
||||||
|
home: 'На главную',
|
||||||
|
services: 'Услуги',
|
||||||
|
contact: 'Контакты',
|
||||||
|
back: 'Назад',
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<html lang="ru">
|
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-blue-950 to-slate-900 flex items-center justify-center px-4 relative overflow-hidden">
|
||||||
<body style={{ margin: 0, fontFamily: 'sans-serif', background: '#f8fafc' }}>
|
{/* Grid pattern */}
|
||||||
<div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '2rem' }}>
|
<div className="absolute inset-0 opacity-5"
|
||||||
<div style={{ textAlign: 'center', maxWidth: '480px' }}>
|
style={{ backgroundImage: 'linear-gradient(#fff 0.5px, transparent 0.5px), linear-gradient(90deg, #fff 0.5px, transparent 0.5px)', backgroundSize: '4rem 4rem' }} />
|
||||||
<div style={{ fontSize: '6rem', fontWeight: 700, color: '#2563eb', lineHeight: 1 }}>404</div>
|
|
||||||
<h1 style={{ fontSize: '1.5rem', fontWeight: 700, color: '#0f172a', margin: '1rem 0 0.5rem' }}>
|
{/* Glow orbs */}
|
||||||
Страница не найдена
|
<div className="absolute top-1/4 left-1/4 w-96 h-96 bg-blue-500/10 rounded-full blur-3xl" />
|
||||||
</h1>
|
<div className="absolute bottom-1/4 right-1/4 w-96 h-96 bg-blue-700/10 rounded-full blur-3xl" />
|
||||||
<p style={{ color: '#64748b', marginBottom: '2rem' }}>
|
|
||||||
Запрошенная страница не существует или была перемещена.
|
<div className="relative z-10 text-center max-w-2xl mx-auto">
|
||||||
</p>
|
{/* 404 */}
|
||||||
<Link href="/ru/"
|
<div className="text-[10rem] sm:text-[14rem] font-bold leading-none select-none"
|
||||||
style={{ display: 'inline-block', padding: '0.75rem 2rem', background: '#2563eb', color: '#fff', borderRadius: '0.5rem', textDecoration: 'none', fontWeight: 600 }}>
|
style={{
|
||||||
На главную
|
background: 'linear-gradient(135deg, #60a5fa, #3b82f6, #60a5fa)',
|
||||||
</Link>
|
backgroundSize: '200% auto',
|
||||||
</div>
|
WebkitBackgroundClip: 'text',
|
||||||
|
WebkitTextFillColor: 'transparent',
|
||||||
|
backgroundClip: 'text',
|
||||||
|
}}>
|
||||||
|
404
|
||||||
</div>
|
</div>
|
||||||
</body>
|
|
||||||
</html>
|
<h1 className="text-2xl sm:text-3xl font-bold text-white mt-2 mb-4">{t.subtitle}</h1>
|
||||||
|
<p className="text-slate-400 text-lg mb-10">{t.description}</p>
|
||||||
|
|
||||||
|
{/* Buttons */}
|
||||||
|
<div className="flex flex-wrap justify-center gap-3 mb-8">
|
||||||
|
<Link href={`/${lang}/`}
|
||||||
|
className="inline-flex items-center gap-2 px-6 py-3 bg-blue-600 hover:bg-blue-500 text-white font-semibold rounded-lg transition-colors">
|
||||||
|
<Home size={18} />
|
||||||
|
{t.home}
|
||||||
|
</Link>
|
||||||
|
<Link href={`/${lang}/uslugi/`}
|
||||||
|
className="inline-flex items-center gap-2 px-6 py-3 bg-slate-800 hover:bg-slate-700 text-white font-semibold rounded-lg border border-slate-700 transition-colors">
|
||||||
|
<Wrench size={18} />
|
||||||
|
{t.services}
|
||||||
|
</Link>
|
||||||
|
<Link href={`/${lang}/kontakty/`}
|
||||||
|
className="inline-flex items-center gap-2 px-6 py-3 bg-slate-800 hover:bg-slate-700 text-white font-semibold rounded-lg border border-slate-700 transition-colors">
|
||||||
|
<Mail size={18} />
|
||||||
|
{t.contact}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Back */}
|
||||||
|
<button
|
||||||
|
onClick={() => window.history.back()}
|
||||||
|
className="inline-flex items-center gap-2 text-slate-400 hover:text-blue-400 transition-colors text-sm">
|
||||||
|
<ArrowLeft size={16} />
|
||||||
|
{t.back}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ export default function Header({ lang }: { lang: string }) {
|
|||||||
|
|
||||||
const links = [
|
const links = [
|
||||||
{ label: d.nav.services, href: `/${lang}/uslugi/` },
|
{ label: d.nav.services, href: `/${lang}/uslugi/` },
|
||||||
{ label: d.nav.about, href: `/${lang}/o-kompanii/` },
|
{ label: d.nav.about, href: `/${lang}/about/` },
|
||||||
|
{ label: d.nav.clients, href: `/${lang}/clients/` },
|
||||||
|
{ label: d.nav.partners, href: `/${lang}/partners/` },
|
||||||
{ label: d.nav.faq, href: `/${lang}/faq/` },
|
{ label: d.nav.faq, href: `/${lang}/faq/` },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import type { Dictionary } from '@/lib/i18n'
|
import type { Dictionary } from '@/lib/i18n'
|
||||||
|
|
||||||
export default function ClientsSection({ d }: { d: Dictionary }) {
|
export default function ClientsSection({ d, standalone }: { d: Dictionary; standalone?: boolean }) {
|
||||||
|
const Title = standalone ? 'h1' : 'h2'
|
||||||
return (
|
return (
|
||||||
<section className="py-16 bg-white border-t border-slate-100">
|
<section className={`${standalone ? 'py-24' : 'py-16 border-t border-slate-100'} bg-white`}>
|
||||||
<div className="max-w-6xl mx-auto px-4 sm:px-6">
|
<div className="max-w-6xl mx-auto px-4 sm:px-6">
|
||||||
<div className="text-center mb-10">
|
<div className="text-center mb-10">
|
||||||
<h2 className="text-2xl sm:text-3xl font-bold text-slate-900 mb-2">{d.clients.title}</h2>
|
<Title className="text-2xl sm:text-3xl font-bold text-slate-900 mb-2">{d.clients.title}</Title>
|
||||||
<p className="text-slate-500">{d.clients.subtitle}</p>
|
<p className="text-slate-500">{d.clients.subtitle}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap justify-center gap-3">
|
<div className="flex flex-wrap justify-center gap-3">
|
||||||
|
|||||||
@@ -7,12 +7,13 @@ const PARTNERS = [
|
|||||||
{ name: 'КОНТУР', sub: 'Электронная отчётность' },
|
{ name: 'КОНТУР', sub: 'Электронная отчётность' },
|
||||||
]
|
]
|
||||||
|
|
||||||
export default function PartnersSection({ d }: { d: Dictionary }) {
|
export default function PartnersSection({ d, standalone }: { d: Dictionary; standalone?: boolean }) {
|
||||||
|
const Title = standalone ? 'h1' : 'h2'
|
||||||
return (
|
return (
|
||||||
<section className="py-16 bg-slate-50 border-t border-slate-100">
|
<section className={`${standalone ? 'py-24' : 'py-16 border-t border-slate-100'} bg-slate-50`}>
|
||||||
<div className="max-w-6xl mx-auto px-4 sm:px-6">
|
<div className="max-w-6xl mx-auto px-4 sm:px-6">
|
||||||
<div className="text-center mb-10">
|
<div className="text-center mb-10">
|
||||||
<h2 className="text-2xl sm:text-3xl font-bold text-slate-900 mb-2">{d.partners.title}</h2>
|
<Title className="text-2xl sm:text-3xl font-bold text-slate-900 mb-2">{d.partners.title}</Title>
|
||||||
<p className="text-slate-500">{d.partners.subtitle}</p>
|
<p className="text-slate-500">{d.partners.subtitle}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap justify-center gap-4">
|
<div className="flex flex-wrap justify-center gap-4">
|
||||||
|
|||||||
12
src/lib/breadcrumbs.ts
Normal file
12
src/lib/breadcrumbs.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
export function breadcrumbSchema(items: { name: string; url: string }[]) {
|
||||||
|
return {
|
||||||
|
'@context': 'https://schema.org',
|
||||||
|
'@type': 'BreadcrumbList',
|
||||||
|
'itemListElement': items.map((item, i) => ({
|
||||||
|
'@type': 'ListItem',
|
||||||
|
'position': i + 1,
|
||||||
|
'name': item.name,
|
||||||
|
'item': item.url,
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@ export const en = {
|
|||||||
nav: {
|
nav: {
|
||||||
services: 'Services',
|
services: 'Services',
|
||||||
about: 'About',
|
about: 'About',
|
||||||
|
clients: 'Clients',
|
||||||
|
partners: 'Partners',
|
||||||
faq: 'FAQ',
|
faq: 'FAQ',
|
||||||
contact: 'Contact',
|
contact: 'Contact',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
export const ru = {
|
export const ru = {
|
||||||
nav: {
|
nav: {
|
||||||
services: 'Услуги',
|
services: 'Услуги',
|
||||||
about: 'О нас',
|
about: 'О компании',
|
||||||
|
clients: 'Клиенты',
|
||||||
|
partners: 'Партнёры',
|
||||||
faq: 'FAQ',
|
faq: 'FAQ',
|
||||||
contact: 'Контакты',
|
contact: 'Контакты',
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user