Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ef904ca952 | |||
| e8dc61f5a1 | |||
| 62f12c8ce1 | |||
| a9cafb629c | |||
| abdc7bd70e | |||
| 7fd1755a7d |
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "docs",
|
||||
"version": "0.6.5-main",
|
||||
"version": "0.6.4-link",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "docs",
|
||||
"version": "0.6.5-main",
|
||||
"version": "0.6.4-link",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@beeline/design-tokens": "^1.31.6",
|
||||
|
||||
+236
-236
@@ -2,7 +2,7 @@ import { defineConfig } from 'vitepress'
|
||||
import { tabsMarkdownPlugin } from 'vitepress-plugin-tabs'
|
||||
import { viteStaticCopy } from 'vite-plugin-static-copy'
|
||||
import { overrideComponents } from './override-components'
|
||||
import { resolve } from 'node:path'
|
||||
import { resolve } from 'path'
|
||||
import { fileURLToPath, URL } from 'node:url'
|
||||
|
||||
const gitlab = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
@@ -41,7 +41,240 @@ const gitlab = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
`
|
||||
|
||||
const new_version = process.env?.VITE_NEW_VERSION;
|
||||
console.log({ base: typeof new_version !== 'undefined' ? '/' : '/docs/' })
|
||||
|
||||
const sidebarConfig = {
|
||||
'/platform/': [
|
||||
{
|
||||
text: 'Платформа Beeline Cloud', link: '/platform/index.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: 'Обзор', link: '/platform/about.md'},
|
||||
{text: 'Техническая поддержка', link: '/platform/support/support-overview.md'},
|
||||
],
|
||||
},
|
||||
],
|
||||
'/start/': [
|
||||
{
|
||||
text: 'Начало работы в Beeline Cloud', link: '/start/index.md',
|
||||
},
|
||||
{text: 'Начать работу', link: '/start/getting-started.md'},
|
||||
{text: 'Бесплатный период', link: '/start/trial.md'},
|
||||
{text: 'Платное использование', link: '/start/organization.md'},
|
||||
],
|
||||
// '/billing/': [
|
||||
|
||||
// ],
|
||||
'/backups/': [
|
||||
{
|
||||
text: 'Резервное копирование', link: '/backups/index.md',
|
||||
},
|
||||
{
|
||||
text: 'Обзор сервиса', link: '/backups/about.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: 'Квоты и лимиты', link: '/backups/backup-quatos.md'},
|
||||
]
|
||||
},
|
||||
{text: 'Резервное копирование виртуальных машин Beeline Cloud', link: '/backups/backup-internal-infra.md'},
|
||||
{text: 'Резервное копирование собственной инфраструктуры в Beeline Cloud', link: '/backups/backup-external-infra.md'},
|
||||
{text: 'Каталог резервных копий', link: '/backups/view-backups.md'},
|
||||
|
||||
],
|
||||
'/vdc/': [
|
||||
{
|
||||
text: 'Виртуальные дата-центры на VMware', link: '/vdc/index.md',
|
||||
},
|
||||
{
|
||||
text: 'Обзор сервиса', link: '/vdc/vdc-overview.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'О сервисе', link: '/vdc/vdc-about.md' },
|
||||
{ text: 'Техническое описание', link: '/vdc/vdc-tech.md' },
|
||||
{ text: 'Квоты и лимиты', link: '/vdc/vdc-quatos.md' },
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'Быстрый старт', link: '/vdc/vdc-getting-started.md'
|
||||
},
|
||||
|
||||
{ text: 'Виртуальные дата-центры', link: '/vdc/vdc-how-to/vdc-index.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'Создание дата-центра', link: '/vdc/vdc-how-to/vdc-create.md' },
|
||||
{ text: 'Вход в дата-центр', link: '/vdc/vdc-how-to/vdc-enter.md' },
|
||||
{ text: 'Управление дата-центром', link: '/vdc/vdc-how-to/vdc-manage.md'},
|
||||
],
|
||||
},
|
||||
{ text: 'Виртуальные машины', link: '/vdc/vdc-how-to/vm/vm-index.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: 'Создание ВМ', link: '/vdc/vdc-how-to/vm/create-vm.md'},
|
||||
{text: 'Создание vApp', link: '/vdc/vdc-how-to/vm/create-vapp.md'},
|
||||
{text: 'Управление состоянием ВМ', link: '/vdc/vdc-how-to/vm/manage-vm.md'},
|
||||
{text: 'Клонирование ВМ', link: '/vdc/vdc-how-to/vm/clone-vm.md'},
|
||||
{text: 'Изменение конфигурации ВМ', link: '/vdc/vdc-how-to/vm/edit-vm.md'},
|
||||
{text: 'Удаление ВМ', link: '/vdc/vdc-how-to/vm/delete-vm.md'},
|
||||
{text: 'Группы размещения', link: '/vdc/vdc-how-to/vm/create-affinity-rules.md'},
|
||||
{text: 'Снимки ВМ', link: '/vdc/vdc-how-to/vm/create-snapshot.md'},
|
||||
{text: 'VMware Tools', link: '/vdc/vdc-how-to/vm/vmware-tools.md'},
|
||||
],
|
||||
},
|
||||
{ text: 'Диски', link: '/vdc/vdc-how-to/disks/disks-index.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: 'Обзор', link: '/vdc/vdc-how-to/disks/about.md'},
|
||||
{text: 'Создание диска', link: '/vdc/vdc-how-to/disks/create-disk.md'},
|
||||
{text: 'Проверка состояния диска', link: '/vdc/vdc-how-to/disks/view-disk.md'},
|
||||
{text: 'Управление выделенными дисками', link: '/vdc/vdc-how-to/disks/attach-disk.md'},
|
||||
{text: 'Изменение политики хранения дисков ВМ', link: '/vdc/vdc-how-to/disks/change-storage-policy-of-vm.md'},
|
||||
{text: 'Редактирование параметров диска', link: '/vdc/vdc-how-to/disks/edit-disk.md'},
|
||||
{text: 'Удаление диска', link: '/vdc/vdc-how-to/disks/delete-disk.md'},
|
||||
],
|
||||
},
|
||||
{ text: 'Сети', link: '/vdc/vdc-how-to/networks/networks-index.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: 'Обзор', link: '/vdc/vdc-how-to/networks/about.md'},
|
||||
{text: 'Настройка доступа к ВМ из интернета', link: '/vdc/vdc-how-to/networks/allow-external-connections-to-vm.md'},
|
||||
{text: 'Подключение ВМ в vApp к сети', link: '/vdc/vdc-how-to/networks/connect-vapp-to-network.md'},
|
||||
{text: 'Подключение ВМ к интернету', link: '/vdc/vdc-how-to/networks/connect-vm-to-network.md'},
|
||||
{text: 'Создание сети в организации и подключение к Edge Gateway', link: '/vdc/vdc-how-to/networks/create-network.md'},
|
||||
{text: 'Подключение сети к Edge Gateway', link: '/vdc/vdc-how-to/networks/connect-to-edge-gateway.md'},
|
||||
{text: 'Создание Pre-Shared Key', link: '/vdc/vdc-how-to/networks/create-psk.md'},
|
||||
{ text: 'Настройка site-to-site подключения с помощью IPSec', link: '/vdc/vdc-how-to/networks/how-to-setup-ipsec-vpn.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: 'Настройка ASAv', link: '/vdc/vdc-how-to/networks/ipsec/asav.md'},
|
||||
{text: 'Настройка CSR 1000v', link: '/vdc/vdc-how-to/networks/ipsec/csr1000v.md'},
|
||||
{text: 'Настройка Fortigate', link: '/vdc/vdc-how-to/networks/ipsec/fortigate.md'},
|
||||
{text: 'Проверить сетевую связанность', link: '/vdc/vdc-how-to/networks/ipsec/check-vpn-status.md'},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{ text: 'Пользователи и роли', link: '/vdc/vdc-how-to/users/users-index.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: 'Ролевая модель', link: '/vdc/vdc-how-to/users/roles.md'},
|
||||
{text: 'Создание пользователя', link: '/vdc/vdc-how-to/users/add-user.md'},
|
||||
{text: 'Изменение пароля пользователя', link: '/vdc/vdc-how-to/users/change-users-password.md'},
|
||||
{text: 'Настройка квот', link: '/vdc/vdc-how-to/users/quotas.md'},
|
||||
],
|
||||
},
|
||||
{ text: 'Двухфакторная аутентификация', link: '/vdc/vdc-how-to/vdc-2fa.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: 'Подключение 2FA', link: '/vdc/vdc-how-to/vdc-2fa-start.md'},
|
||||
{text: 'Управление 2FA', link: '/vdc/vdc-how-to/vdc-2fa-manage.md'},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
// { text: 'Тарификация', link: '/vdc/vdc-tarif.md' },
|
||||
'/compute/': [
|
||||
{
|
||||
text: 'Виртуальные машины', link: '/compute/index.md',
|
||||
},
|
||||
{ text: 'Обзор сервиса', link: '/compute/compute-overview-index.md' ,
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'Техническое описание', link: '/compute/compute-overview.md' },
|
||||
{ text: 'Квоты и лимиты', link: '/compute/compute-quatos.md' },
|
||||
]
|
||||
},
|
||||
{text: 'Быстрый старт', link: '/compute/compute-getting-started.md', excludeFromIndex: true },
|
||||
{ text: 'Виртуальные машины', link: '/compute/compute-how-to/compute-index.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'Создание ВМ', link: '/compute/compute-how-to/compute-servers-create.md' },
|
||||
{ text: 'Создание ВМ джамп-хоста', link: '/compute/compute-how-to/compute-servers-jump-create.md' },
|
||||
{ text: 'Подключение к ВМ', link: '/compute/compute-how-to/compute-connect-index.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'Подключение по SSH по внешнему IP-адресу с помощью ключевой пары', link: '/compute/compute-how-to/compute-connect-public.md'},
|
||||
{ text: 'Подключение по SSH по внутреннему IP-адресу с помощью ключевой пары', link: '/compute/compute-how-to/compute-connect-inside.md' },
|
||||
{ text: 'Подключение по SSH по логину и паролю', link: '/compute/compute-how-to/compute-connect-pwd.md' },
|
||||
]
|
||||
},
|
||||
{ text: 'Управление ВМ', link: '/compute/compute-how-to/compute-servers-manage.md' },
|
||||
],
|
||||
},
|
||||
{ text: 'Диски', link: '/compute/compute-how-to/compute-disks/compute-disk-index.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'Обзор', link: '/compute/compute-how-to/compute-disks/compute-disk-about.md' },
|
||||
{ text: 'Создание диска', link: '/compute/compute-how-to/compute-disks/compute-disk-create.md' },
|
||||
{ text: 'Управление дисками', link: '/compute/compute-how-to/compute-disks/compute-disk-manage.md' },
|
||||
{ text: 'Удаление диска', link: '/compute/compute-how-to/compute-disks/compute-disk-del.md' },
|
||||
]
|
||||
},
|
||||
{ text: 'IP-адреса', link: '/compute/compute-how-to/compute-ip/compute-ip-index.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'Обзор', link: '/compute/compute-how-to/compute-ip/compute-ip-about.md' },
|
||||
{ text: 'Просмотр IP-адресов', link: '/compute/compute-how-to/compute-ip/compute-ip-view.md' },
|
||||
{ text: 'Создание IP-адреса', link: '/compute/compute-how-to/compute-ip/compute-ip-create.md' },
|
||||
{ text: 'Управление IP-адресами', link: '/compute/compute-how-to/compute-ip/compute-ip-manager.md' },
|
||||
{ text: 'Удаление IP-адреса', link: '/compute/compute-how-to/compute-ip/compute-ip-del.md' },
|
||||
]
|
||||
},
|
||||
{ text: 'Группы размещения', link: '/compute/compute-how-to/compute-placement-groups/compute-placement-groups-index.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'Обзор', link: '/compute/compute-how-to/compute-placement-groups/compute-placement-groups-about.md'},
|
||||
{ text: 'Создание группы размещения', link: '/compute/compute-how-to/compute-placement-groups/compute-placement-groups-create.md'},
|
||||
{ text: 'Управление группами размещения', link: '/compute/compute-how-to/compute-placement-groups/compute-placement-groups-manager.md' },
|
||||
{ text: 'Удаление группы размещения', link: '/compute/compute-how-to/compute-placement-groups/compute-placement-groups-del.md' },
|
||||
]
|
||||
},
|
||||
{ text: 'Сети', link: '/compute/compute-how-to/compute-network/compute-network-index.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'Настройка site-to-site VPN с помощью VyOS', link: '/compute/compute-how-to/compute-network/compute-vpn-vyos.md' },
|
||||
{ text: 'Подключение ВМ закрытого контура к интернету', link: '/compute/compute-how-to/compute-network/compute-network-inside.md' },
|
||||
],
|
||||
excludeFromIndex: true,
|
||||
},
|
||||
],
|
||||
'/admin/': [
|
||||
{
|
||||
text: 'Администрирование', link: '/admin/index.md',
|
||||
},
|
||||
{text: 'Управление ключевыми парами', link: '/admin/ssh.md'},
|
||||
],
|
||||
'/vdi/': [
|
||||
{
|
||||
text: 'Виртуальные рабочие столы', link: '/vdi/index.md',
|
||||
},
|
||||
{
|
||||
text: 'Обзор сервиса', link: '/vdi/vdi-overview.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'О сервисе', link: '/vdi/vdi-about.md' },
|
||||
{ text: 'Техническое описание', link: '/vdi/vdi-tech.md' },
|
||||
{ text: 'Квоты и лимиты', link: '/vdi/vdi-quatos.md' },
|
||||
{ text: 'Тарификация', link: '/vdi/vdi-tarif.md' },
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'Заказ виртуальных рабочих столов', link: '/vdi/vdi-how-to/vdi-create.md'
|
||||
},
|
||||
{
|
||||
text: 'Настройка сервиса', link: '/vdi/vdi-how-to/vdi-nastroika.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'Настройка интеграции с Active Directory', link: '/vdi/vdi-how-to/vdi-connect-to-ad.md' },
|
||||
{ text: 'Настройка сети', link: '/vdi/vdi-how-to/vdi-interconnect.md' },
|
||||
]
|
||||
},
|
||||
// {
|
||||
// text: 'Gold-образ', link: '/vdi/vdi-how-to/vdi-gold.md'
|
||||
// },
|
||||
{
|
||||
text: 'Подключение к виртуальному рабочему месту', link: '/vdi/vdi-how-to/vdi-connect.md'
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default defineConfig({
|
||||
@@ -140,240 +373,7 @@ export default defineConfig({
|
||||
outline: {
|
||||
label: 'Содержание'
|
||||
},
|
||||
sidebar: {
|
||||
'/platform/': [
|
||||
{
|
||||
text: 'Платформа Beeline Cloud', link: '/platform/index.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: 'Обзор', link: '/platform/about.md'},
|
||||
{text: 'Техническая поддержка', link: '/platform/support/support-overview.md'},
|
||||
],
|
||||
},
|
||||
],
|
||||
'/start/': [
|
||||
{
|
||||
text: 'Начало работы в Beeline Cloud', link: '/start/index.md',
|
||||
},
|
||||
{text: 'Начать работу', link: '/start/getting-started.md'},
|
||||
{text: 'Бесплатный период', link: '/start/trial.md'},
|
||||
{text: 'Платное использование', link: '/start/organization.md'},
|
||||
],
|
||||
// '/billing/': [
|
||||
|
||||
// ],
|
||||
'/backups/': [
|
||||
{
|
||||
text: 'Резервное копирование', link: '/backups/index.md',
|
||||
},
|
||||
{
|
||||
text: 'Обзор сервиса', link: '/backups/backups-overview.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: 'О сервисе', link: '/backups/about.md'},
|
||||
{text: 'Квоты и лимиты', link: '/backups/backup-quatos.md'},
|
||||
]
|
||||
},
|
||||
{text: 'Резервное копирование виртуальных машин Beeline Cloud', link: '/backups/backup-internal-infra.md'},
|
||||
{text: 'Резервное копирование собственной инфраструктуры в Beeline Cloud', link: '/backups/backup-external-infra.md'},
|
||||
{text: 'Каталог резервных копий', link: '/backups/view-backups.md'},
|
||||
|
||||
],
|
||||
'/vdc/': [
|
||||
{
|
||||
text: 'Виртуальные дата-центры на VMware', link: '/vdc/index.md',
|
||||
},
|
||||
{
|
||||
text: 'Обзор сервиса', link: '/vdc/vdc-overview.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'О сервисе', link: '/vdc/vdc-about.md' },
|
||||
{ text: 'Техническое описание', link: '/vdc/vdc-tech.md' },
|
||||
{ text: 'Квоты и лимиты', link: '/vdc/vdc-quatos.md' },
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'Быстрый старт', link: '/vdc/vdc-getting-started.md'
|
||||
},
|
||||
|
||||
{ text: 'Виртуальные дата-центры', link: '/vdc/vdc-how-to/vdc-index.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'Создание дата-центра', link: '/vdc/vdc-how-to/vdc-create.md' },
|
||||
{ text: 'Вход в дата-центр', link: '/vdc/vdc-how-to/vdc-enter.md' },
|
||||
{ text: 'Управление дата-центром', link: '/vdc/vdc-how-to/vdc-manage.md'},
|
||||
],
|
||||
},
|
||||
{ text: 'Виртуальные машины', link: '/vdc/vdc-how-to/vm/vm-index.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: 'Создание ВМ', link: '/vdc/vdc-how-to/vm/create-vm.md'},
|
||||
{text: 'Создание vApp', link: '/vdc/vdc-how-to/vm/create-vapp.md'},
|
||||
{text: 'Управление состоянием ВМ', link: '/vdc/vdc-how-to/vm/manage-vm.md'},
|
||||
{text: 'Клонирование ВМ', link: '/vdc/vdc-how-to/vm/clone-vm.md'},
|
||||
{text: 'Изменение конфигурации ВМ', link: '/vdc/vdc-how-to/vm/edit-vm.md'},
|
||||
{text: 'Удаление ВМ', link: '/vdc/vdc-how-to/vm/delete-vm.md'},
|
||||
{text: 'Группы размещения', link: '/vdc/vdc-how-to/vm/create-affinity-rules.md'},
|
||||
{text: 'Снимки ВМ', link: '/vdc/vdc-how-to/vm/create-snapshot.md'},
|
||||
{text: 'VMware Tools', link: '/vdc/vdc-how-to/vm/vmware-tools.md'},
|
||||
],
|
||||
},
|
||||
{ text: 'Диски', link: '/vdc/vdc-how-to/disks/disks-index.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: 'Обзор', link: '/vdc/vdc-how-to/disks/about.md'},
|
||||
{text: 'Создание диска', link: '/vdc/vdc-how-to/disks/create-disk.md'},
|
||||
{text: 'Проверка состояния диска', link: '/vdc/vdc-how-to/disks/view-disk.md'},
|
||||
{text: 'Управление выделенными дисками', link: '/vdc/vdc-how-to/disks/attach-disk.md'},
|
||||
{text: 'Изменение политики хранения дисков ВМ', link: '/vdc/vdc-how-to/disks/change-storage-policy-of-vm.md'},
|
||||
{text: 'Редактирование параметров диска', link: '/vdc/vdc-how-to/disks/edit-disk.md'},
|
||||
{text: 'Удаление диска', link: '/vdc/vdc-how-to/disks/delete-disk.md'},
|
||||
],
|
||||
},
|
||||
{ text: 'Сети', link: '/vdc/vdc-how-to/networks/networks-index.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: 'Обзор', link: '/vdc/vdc-how-to/networks/about.md'},
|
||||
{text: 'Настройка доступа к ВМ из интернета', link: '/vdc/vdc-how-to/networks/allow-external-connections-to-vm.md'},
|
||||
{text: 'Подключение ВМ в vApp к сети', link: '/vdc/vdc-how-to/networks/connect-vapp-to-network.md'},
|
||||
{text: 'Подключение ВМ к интернету', link: '/vdc/vdc-how-to/networks/connect-vm-to-network.md'},
|
||||
{text: 'Создание сети в организации и подключение к Edge Gateway', link: '/vdc/vdc-how-to/networks/create-network.md'},
|
||||
{text: 'Подключение сети к Edge Gateway', link: '/vdc/vdc-how-to/networks/connect-to-edge-gateway.md'},
|
||||
{text: 'Создание Pre-Shared Key', link: '/vdc/vdc-how-to/networks/create-psk.md'},
|
||||
{ text: 'Настройка site-to-site подключения с помощью IPSec', link: '/vdc/vdc-how-to/networks/how-to-setup-ipsec-vpn.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: 'Настройка IPSec VPN', link: '/vdc/vdc-how-to/networks/ipsec/setup-ipsec-vpn.md'},
|
||||
{text: 'Настройка ASAv', link: '/vdc/vdc-how-to/networks/ipsec/asav.md'},
|
||||
{text: 'Настройка CSR 1000v', link: '/vdc/vdc-how-to/networks/ipsec/csr1000v.md'},
|
||||
{text: 'Настройка Fortigate', link: '/vdc/vdc-how-to/networks/ipsec/fortigate.md'},
|
||||
{text: 'Проверить сетевую связанность', link: '/vdc/vdc-how-to/networks/ipsec/check-vpn-status.md'},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{ text: 'Пользователи и роли', link: '/vdc/vdc-how-to/users/users-index.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: 'Ролевая модель', link: '/vdc/vdc-how-to/users/roles.md'},
|
||||
{text: 'Создание пользователя', link: '/vdc/vdc-how-to/users/add-user.md'},
|
||||
{text: 'Изменение пароля пользователя', link: '/vdc/vdc-how-to/users/change-users-password.md'},
|
||||
{text: 'Настройка квот', link: '/vdc/vdc-how-to/users/quotas.md'},
|
||||
],
|
||||
},
|
||||
{ text: 'Двухфакторная аутентификация', link: '/vdc/vdc-how-to/vdc-2fa.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: 'Подключение 2FA', link: '/vdc/vdc-how-to/vdc-2fa-start.md'},
|
||||
{text: 'Управление 2FA', link: '/vdc/vdc-how-to/vdc-2fa-manage.md'},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
// { text: 'Тарификация', link: '/vdc/vdc-tarif.md' },
|
||||
'/compute/': [
|
||||
{
|
||||
text: 'Виртуальные машины', link: '/compute/index.md',
|
||||
},
|
||||
{ text: 'Обзор сервиса', link: '/compute/compute-overview-index.md' ,
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'Техническое описание', link: '/compute/compute-overview.md' },
|
||||
{ text: 'Квоты и лимиты', link: '/compute/compute-quatos.md' },
|
||||
]
|
||||
},
|
||||
{text: 'Быстрый старт', link: '/compute/compute-getting-started.md'},
|
||||
{ text: 'Виртуальные машины', link: '/compute/compute-how-to/compute-index.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'Создание ВМ', link: '/compute/compute-how-to/compute-servers-create.md' },
|
||||
{ text: 'Создание ВМ джамп-хоста', link: '/compute/compute-how-to/compute-servers-jump-create.md' },
|
||||
{ text: 'Подключение к ВМ', link: '/compute/compute-how-to/compute-connect-index.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'Подключение по SSH по внешнему IP-адресу с помощью ключевой пары', link: '/compute/compute-how-to/compute-connect-public.md'},
|
||||
{ text: 'Подключение по SSH по внутреннему IP-адресу с помощью ключевой пары', link: '/compute/compute-how-to/compute-connect-inside.md' },
|
||||
{ text: 'Подключение по SSH по логину и паролю', link: '/compute/compute-how-to/compute-connect-pwd.md' },
|
||||
]
|
||||
},
|
||||
{ text: 'Управление ВМ', link: '/compute/compute-how-to/compute-servers-manage.md' },
|
||||
],
|
||||
},
|
||||
{ text: 'Диски', link: '/compute/compute-how-to/compute-disks/compute-disk-index.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'Обзор', link: '/compute/compute-how-to/compute-disks/compute-disk-about.md' },
|
||||
{ text: 'Создание диска', link: '/compute/compute-how-to/compute-disks/compute-disk-create.md' },
|
||||
{ text: 'Управление дисками', link: '/compute/compute-how-to/compute-disks/compute-disk-manage.md' },
|
||||
{ text: 'Удаление диска', link: '/compute/compute-how-to/compute-disks/compute-disk-del.md' },
|
||||
]
|
||||
},
|
||||
{ text: 'IP-адреса', link: '/compute/compute-how-to/compute-ip/compute-ip-index.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'Обзор', link: '/compute/compute-how-to/compute-ip/compute-ip-about.md' },
|
||||
{ text: 'Просмотр IP-адресов', link: '/compute/compute-how-to/compute-ip/compute-ip-view.md' },
|
||||
{ text: 'Создание IP-адреса', link: '/compute/compute-how-to/compute-ip/compute-ip-create.md' },
|
||||
{ text: 'Управление IP-адресами', link: '/compute/compute-how-to/compute-ip/compute-ip-manager.md' },
|
||||
{ text: 'Удаление IP-адреса', link: '/compute/compute-how-to/compute-ip/compute-ip-del.md' },
|
||||
]
|
||||
},
|
||||
{ text: 'Группы размещения', link: '/compute/compute-how-to/compute-placement-groups/compute-placement-groups-index.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'Обзор', link: '/compute/compute-how-to/compute-placement-groups/compute-placement-groups-about.md'},
|
||||
{ text: 'Создание группы размещения', link: '/compute/compute-how-to/compute-placement-groups/compute-placement-groups-create.md'},
|
||||
{ text: 'Управление группами размещения', link: '/compute/compute-how-to/compute-placement-groups/compute-placement-groups-manager.md' },
|
||||
{ text: 'Удаление группы размещения', link: '/compute/compute-how-to/compute-placement-groups/compute-placement-groups-del.md' },
|
||||
]
|
||||
},
|
||||
{ text: 'Сети', link: '/compute/compute-how-to/compute-network/compute-network-index.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'Настройка site-to-site VPN с помощью VyOS', link: '/compute/compute-how-to/compute-network/compute-vpn-vyos.md' },
|
||||
{ text: 'Подключение ВМ закрытого контура к интернету', link: '/compute/compute-how-to/compute-network/compute-network-inside.md' },
|
||||
],
|
||||
},
|
||||
],
|
||||
'/admin/': [
|
||||
{
|
||||
text: 'Администрирование', link: '/admin/index.md',
|
||||
},
|
||||
{text: 'Управление ключевыми парами', link: '/admin/ssh.md'},
|
||||
],
|
||||
'/vdi/': [
|
||||
{
|
||||
text: 'Виртуальные рабочие столы', link: '/vdi/index.md',
|
||||
},
|
||||
{
|
||||
text: 'Обзор сервиса', link: '/vdi/vdi-overview.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'О сервисе', link: '/vdi/vdi-about.md' },
|
||||
{ text: 'Техническое описание', link: '/vdi/vdi-tech.md' },
|
||||
{ text: 'Квоты и лимиты', link: '/vdi/vdi-quatos.md' },
|
||||
{ text: 'Тарификация', link: '/vdi/vdi-tarif.md' },
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'Заказ виртуальных рабочих столов', link: '/vdi/vdi-how-to/vdi-create.md'
|
||||
},
|
||||
{
|
||||
text: 'Настройка сервиса', link: '/vdi/vdi-how-to/vdi-nastroika.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'Настройка интеграции с Active Directory', link: '/vdi/vdi-how-to/vdi-connect-to-ad.md' },
|
||||
{ text: 'Настройка сети', link: '/vdi/vdi-how-to/vdi-interconnect.md' },
|
||||
]
|
||||
},
|
||||
// {
|
||||
// text: 'Gold-образ', link: '/vdi/vdi-how-to/vdi-gold.md'
|
||||
// },
|
||||
{
|
||||
text: 'Подключение к виртуальному рабочему месту', link: '/vdi/vdi-how-to/vdi-connect.md'
|
||||
},
|
||||
],
|
||||
},
|
||||
sidebar: sidebarConfig,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import type { Plugin } from 'vite'
|
||||
import type { SidebarItem } from './utils/types'
|
||||
import { processAllFiles } from './hook/process-files'
|
||||
|
||||
export const autoSectionLinksPlugin = (
|
||||
srcDir: string,
|
||||
sidebarConfig: Record<string, SidebarItem[]>
|
||||
): Plugin => ({
|
||||
name: 'auto-section-links',
|
||||
buildStart: () => {
|
||||
processAllFiles(srcDir, sidebarConfig)
|
||||
},
|
||||
configureServer: () => {
|
||||
processAllFiles(srcDir, sidebarConfig)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,4 @@
|
||||
export const INDEX_FILE = 'index.md'
|
||||
export const INDEX_FILE_PATTERN = /-index\.md$|index\.md$/
|
||||
export const FRONTMATTER_REGEX = /^---\s*\n([\s\S]*?)\n---/
|
||||
export const SECTION_LINK_KEYS = ['title', 'link', 'description'] as const
|
||||
@@ -0,0 +1,36 @@
|
||||
import { existsSync } from 'fs'
|
||||
import { relative, resolve } from 'path'
|
||||
import type { SidebarItem } from '../utils/types'
|
||||
import { findMarkdownFiles } from '../utils/file-finder'
|
||||
import { processIndexFile, processPageWithItems } from '../utils/file-processor'
|
||||
import { INDEX_FILE, INDEX_FILE_PATTERN } from '../constants'
|
||||
|
||||
export const processAllFiles = (
|
||||
srcDir: string,
|
||||
sidebarConfig: Record<string, SidebarItem[]>
|
||||
) => {
|
||||
const srcPath = resolve(srcDir)
|
||||
|
||||
for (const [folderPath, items] of Object.entries(sidebarConfig)) {
|
||||
const normalizedFolder = folderPath.replace(/^\/+|\/+$/g, '')
|
||||
const folderFullPath = resolve(srcPath, normalizedFolder)
|
||||
|
||||
if (!existsSync(folderFullPath)) {
|
||||
continue
|
||||
}
|
||||
|
||||
const mdFiles = findMarkdownFiles(folderFullPath)
|
||||
|
||||
for (const filePath of mdFiles) {
|
||||
const fileName = filePath.split(/[/\\]/).pop() || ''
|
||||
const relativePath = relative(srcPath, filePath).replace(/\\/g, '/')
|
||||
const normalizedPath = relativePath.startsWith('/') ? relativePath : `/${relativePath}`
|
||||
|
||||
if (fileName === INDEX_FILE) {
|
||||
processIndexFile(filePath, items, srcPath)
|
||||
} else if (INDEX_FILE_PATTERN.test(fileName) && fileName !== INDEX_FILE) {
|
||||
processPageWithItems(filePath, items, srcPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { readFileSync, existsSync } from 'fs'
|
||||
import { resolve } from 'path'
|
||||
import { parseFrontmatter } from './frontmatter'
|
||||
|
||||
export const getPageDescription = (filePath: string, srcDir: string) => {
|
||||
try {
|
||||
const fullPath = resolve(srcDir, filePath.replace(/^\//, ''))
|
||||
if (!existsSync(fullPath)) return undefined
|
||||
|
||||
const fileContent = readFileSync(fullPath, 'utf-8')
|
||||
const { frontmatter } = parseFrontmatter(fileContent)
|
||||
|
||||
return frontmatter.description as string | undefined
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { readdirSync, statSync } from 'fs'
|
||||
import { join } from 'path'
|
||||
|
||||
export const findMarkdownFiles = (dir: string) => {
|
||||
const files: string[] = []
|
||||
|
||||
try {
|
||||
const entries = readdirSync(dir)
|
||||
|
||||
for (const entry of entries) {
|
||||
const fullPath = join(dir, entry)
|
||||
const stat = statSync(fullPath)
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
files.push(...findMarkdownFiles(fullPath))
|
||||
} else if (entry.endsWith('.md')) {
|
||||
files.push(fullPath)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
|
||||
return files
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
import { readFileSync, writeFileSync, existsSync } from 'fs'
|
||||
import { relative, resolve } from 'path'
|
||||
import type { SidebarItem } from './types'
|
||||
import { parseFrontmatter, stringifyFrontmatter } from './frontmatter'
|
||||
import { mergeSectionLinks, extractTopLevelLinks, extractItemsForPage } from './links'
|
||||
import { normalizeLink } from './path-utils'
|
||||
import { SectionLinkListItem } from '../../theme/components/SectionLinkList/SectionLinkList.types'
|
||||
|
||||
const removeDuplicates = (links: SectionLinkListItem[]) => {
|
||||
const result: SectionLinkListItem[] = []
|
||||
const seenLinks = new Set<string>()
|
||||
|
||||
for (const link of links) {
|
||||
if (link.link) {
|
||||
const normalized = normalizeLink(link.link)
|
||||
if (!seenLinks.has(normalized)) {
|
||||
seenLinks.add(normalized)
|
||||
result.push(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
const hasChanges = (
|
||||
existingLinks: SectionLinkListItem[],
|
||||
mergedLinks: SectionLinkListItem[]
|
||||
) => {
|
||||
if (existingLinks.length !== mergedLinks.length) return true
|
||||
|
||||
const existingSet = new Set(
|
||||
existingLinks
|
||||
.filter(link => link.link)
|
||||
.map(link => `${normalizeLink(link.link!)}|${link.title}`)
|
||||
)
|
||||
|
||||
const mergedSet = new Set(
|
||||
mergedLinks
|
||||
.filter(link => link.link)
|
||||
.map(link => `${normalizeLink(link.link!)}|${link.title}`)
|
||||
)
|
||||
|
||||
if (existingSet.size !== mergedSet.size) return true
|
||||
|
||||
for (const item of existingSet) {
|
||||
if (!mergedSet.has(item)) return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
export const processIndexFile = (
|
||||
filePath: string,
|
||||
sidebarItems: SidebarItem[],
|
||||
srcDir: string
|
||||
) => {
|
||||
if (!existsSync(filePath)) return false
|
||||
|
||||
try {
|
||||
const fileContent = readFileSync(filePath, 'utf-8')
|
||||
const { frontmatter, content } = parseFrontmatter(fileContent)
|
||||
|
||||
const relativePath = relative(srcDir, filePath).replace(/\\/g, '/')
|
||||
const normalizedPath = relativePath.startsWith('/') ? relativePath : `/${relativePath}`
|
||||
|
||||
const existingLinks = removeDuplicates(
|
||||
Array.isArray(frontmatter.section_links) ? [...frontmatter.section_links] : []
|
||||
)
|
||||
const newLinks = extractTopLevelLinks(sidebarItems, normalizedPath, srcDir)
|
||||
|
||||
if (!newLinks.length) return false
|
||||
|
||||
const mergedLinks = removeDuplicates(mergeSectionLinks(existingLinks, newLinks))
|
||||
|
||||
if (!hasChanges(existingLinks, mergedLinks)) return false
|
||||
|
||||
frontmatter.section_links = mergedLinks
|
||||
writeFileSync(filePath, stringifyFrontmatter(frontmatter, content), 'utf-8')
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export const processPageWithItems = (
|
||||
filePath: string,
|
||||
sidebarItems: SidebarItem[],
|
||||
srcDir: string
|
||||
) => {
|
||||
if (!existsSync(filePath)) return false
|
||||
|
||||
try {
|
||||
const fileContent = readFileSync(filePath, 'utf-8')
|
||||
const { frontmatter, content } = parseFrontmatter(fileContent)
|
||||
|
||||
const relativePath = relative(srcDir, filePath).replace(/\\/g, '/')
|
||||
const normalizedPath = relativePath.startsWith('/') ? relativePath : `/${relativePath}`
|
||||
|
||||
const existingLinks = removeDuplicates(
|
||||
Array.isArray(frontmatter.section_links) ? [...frontmatter.section_links] : []
|
||||
)
|
||||
const newLinks = extractItemsForPage(sidebarItems, normalizedPath, srcDir)
|
||||
|
||||
const mergedLinks = newLinks.length > 0
|
||||
? removeDuplicates(mergeSectionLinks(existingLinks, newLinks))
|
||||
: []
|
||||
|
||||
if (!hasChanges(existingLinks, mergedLinks)) return false
|
||||
|
||||
frontmatter.section_links = mergedLinks
|
||||
writeFileSync(filePath, stringifyFrontmatter(frontmatter, content), 'utf-8')
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
import type { Frontmatter } from './types'
|
||||
import { SECTION_LINK_KEYS, FRONTMATTER_REGEX } from '../constants'
|
||||
import { SectionLinkListItem } from '../../theme/components/SectionLinkList/SectionLinkList.types'
|
||||
|
||||
export const parseFrontmatter = (content: string) => {
|
||||
const frontmatterMatch = content.match(FRONTMATTER_REGEX)
|
||||
if (!frontmatterMatch) {
|
||||
const cleanContent = content.replace(/^---[\s\S]*?---\s*\n*/g, '').trim()
|
||||
return { frontmatter: {}, content: cleanContent || content }
|
||||
}
|
||||
|
||||
const [, frontmatterText] = frontmatterMatch
|
||||
const frontmatterEnd = frontmatterMatch[0].length
|
||||
let cleanContent = content.slice(frontmatterEnd)
|
||||
cleanContent = cleanContent.replace(/^---[\s\S]*?---\s*\n*/g, '').trim()
|
||||
if (!cleanContent) {
|
||||
cleanContent = content.slice(frontmatterEnd).replace(/^---[\s\S]*?---\s*\n*/g, '').trim()
|
||||
}
|
||||
|
||||
const frontmatter: Frontmatter = {}
|
||||
const lines = frontmatterText.split('\n')
|
||||
let currentKey: string | undefined
|
||||
let currentValue: SectionLinkListItem[] = []
|
||||
let currentItem: Record<string, string> | undefined
|
||||
let inArray = false
|
||||
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim()
|
||||
if (trimmed && !trimmed.startsWith('#')) {
|
||||
const lineIndent = line.match(/^(\s*)/)?.[1]?.length ?? 0
|
||||
|
||||
if (lineIndent === 0 && trimmed.includes(':')) {
|
||||
if (inArray && currentKey) {
|
||||
frontmatter[currentKey] = currentValue
|
||||
currentValue = []
|
||||
inArray = false
|
||||
}
|
||||
|
||||
const colonMatch = trimmed.match(/^([^:]+):\s*(.*)$/)
|
||||
if (colonMatch) {
|
||||
const [, key, value] = colonMatch
|
||||
currentKey = key.trim()
|
||||
const trimmedValue = value.trim()
|
||||
|
||||
if (trimmedValue === '') {
|
||||
inArray = true
|
||||
currentValue = []
|
||||
} else {
|
||||
frontmatter[currentKey] = trimmedValue.replace(/^["']|["']$/g, '')
|
||||
}
|
||||
}
|
||||
} else if (lineIndent === 2 && trimmed.startsWith('- ')) {
|
||||
if (!inArray && currentKey) {
|
||||
inArray = true
|
||||
currentValue = []
|
||||
}
|
||||
|
||||
if (currentItem && currentItem.title && currentItem.link !== undefined) {
|
||||
const link: SectionLinkListItem = {
|
||||
title: currentItem.title,
|
||||
link: currentItem.link,
|
||||
description: currentItem.description
|
||||
}
|
||||
if (!currentValue.some(item => item.link === link.link)) {
|
||||
currentValue.push(link)
|
||||
}
|
||||
}
|
||||
|
||||
const itemText = trimmed.slice(2).trim()
|
||||
if (itemText.includes(':')) {
|
||||
currentItem = {}
|
||||
const parts = itemText.split(',').map(p => p.trim())
|
||||
for (const part of parts) {
|
||||
const colonMatch = part.match(/^(\w+):\s*(.+)$/)
|
||||
if (colonMatch) {
|
||||
const [, key, value] = colonMatch
|
||||
if (SECTION_LINK_KEYS.includes(key as typeof SECTION_LINK_KEYS[number])) {
|
||||
currentItem[key] = value.replace(/^["']|["']$/g, '')
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
currentValue.push({ title: itemText, link: '' })
|
||||
}
|
||||
} else if (lineIndent >= 4 && currentItem && trimmed.includes(':')) {
|
||||
const colonIndex = trimmed.indexOf(':')
|
||||
if (colonIndex !== -1) {
|
||||
const key = trimmed.substring(0, colonIndex).trim()
|
||||
const value = trimmed.substring(colonIndex + 1).trim()
|
||||
if (SECTION_LINK_KEYS.includes(key as typeof SECTION_LINK_KEYS[number])) {
|
||||
currentItem[key] = value === '' ? '' : value.replace(/^["']|["']$/g, '')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (currentItem && inArray && currentItem.title && currentItem.link !== undefined) {
|
||||
const link: SectionLinkListItem = {
|
||||
title: currentItem.title,
|
||||
link: currentItem.link,
|
||||
description: currentItem.description
|
||||
}
|
||||
if (!currentValue.some(item => item.link === link.link)) {
|
||||
currentValue.push(link)
|
||||
}
|
||||
}
|
||||
if (inArray && currentKey) {
|
||||
frontmatter[currentKey] = currentValue
|
||||
}
|
||||
|
||||
return { frontmatter, content: cleanContent || content }
|
||||
}
|
||||
|
||||
export const stringifyFrontmatter = (frontmatter: Frontmatter, content: string) => {
|
||||
const lines: string[] = []
|
||||
|
||||
for (const [key, value] of Object.entries(frontmatter)) {
|
||||
if (Array.isArray(value)) {
|
||||
lines.push(`${key}:`)
|
||||
for (const item of value) {
|
||||
if (typeof item === 'object' && item) {
|
||||
const link = item as SectionLinkListItem
|
||||
lines.push(` - title: ${link.title ?? ''}`)
|
||||
if (link.link) lines.push(` link: ${link.link}`)
|
||||
if ('description' in link && link.description !== undefined && link.description !== null && String(link.description).trim() !== '') {
|
||||
lines.push(` description: ${String(link.description)}`)
|
||||
}
|
||||
} else {
|
||||
lines.push(` - ${item}`)
|
||||
}
|
||||
}
|
||||
} else if (value) {
|
||||
const strValue = String(value)
|
||||
const needsQuotes = strValue.includes(':') || (strValue.includes(' ') && !strValue.startsWith('"')) || strValue === ''
|
||||
const escaped = strValue.replace(/"/g, '\\"')
|
||||
lines.push(`${key}: ${needsQuotes ? `"${escaped}"` : strValue}`)
|
||||
}
|
||||
}
|
||||
|
||||
return `---\n${lines.join('\n')}\n---\n\n${content}`
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
import type { SidebarItem } from './types'
|
||||
import { getPageDescription } from './descriptions'
|
||||
import { normalizeLink } from './path-utils'
|
||||
import { SectionLinkListItem } from '../../theme/components/SectionLinkList/SectionLinkList.types'
|
||||
|
||||
export const mergeSectionLinks = (
|
||||
existingLinks: SectionLinkListItem[],
|
||||
newLinks: SectionLinkListItem[]
|
||||
) => {
|
||||
const existingLinksMap = new Map<string, SectionLinkListItem>()
|
||||
const result: SectionLinkListItem[] = []
|
||||
const processedLinks = new Set<string>()
|
||||
|
||||
for (const existingLink of existingLinks) {
|
||||
if (existingLink.link) {
|
||||
const normalizedLink = normalizeLink(existingLink.link)
|
||||
if (!existingLinksMap.has(normalizedLink)) {
|
||||
existingLinksMap.set(normalizedLink, existingLink)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const newLink of newLinks) {
|
||||
if (newLink.link) {
|
||||
const normalizedLink = normalizeLink(newLink.link)
|
||||
|
||||
if (!processedLinks.has(normalizedLink)) {
|
||||
processedLinks.add(normalizedLink)
|
||||
|
||||
if (existingLinksMap.has(normalizedLink)) {
|
||||
const existingLink = existingLinksMap.get(normalizedLink)!
|
||||
result.push({ ...existingLink, title: newLink.title })
|
||||
} else {
|
||||
result.push(newLink)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export const extractTopLevelLinks = (
|
||||
items: SidebarItem[],
|
||||
currentIndexPath: string,
|
||||
srcDir: string
|
||||
) => {
|
||||
const links: SectionLinkListItem[] = []
|
||||
const normalizedCurrentPath = normalizeLink(currentIndexPath)
|
||||
|
||||
for (const item of items) {
|
||||
if ('excludeFromIndex' in item && item.excludeFromIndex) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (item.link) {
|
||||
const normalizedItemPath = normalizeLink(item.link)
|
||||
if (normalizedItemPath === normalizedCurrentPath) {
|
||||
continue
|
||||
}
|
||||
|
||||
links.push({
|
||||
title: item.text || '',
|
||||
link: item.link,
|
||||
description: getPageDescription(item.link, srcDir)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return links
|
||||
}
|
||||
|
||||
const findItemsRecursive = (
|
||||
itemsList: SidebarItem[],
|
||||
pagePath: string,
|
||||
srcDir: string
|
||||
): SectionLinkListItem[] => {
|
||||
const normalizedPagePath = normalizeLink(pagePath)
|
||||
const links: SectionLinkListItem[] = []
|
||||
|
||||
for (const item of itemsList) {
|
||||
if (item.link) {
|
||||
const normalizedItemPath = normalizeLink(item.link)
|
||||
|
||||
if (normalizedItemPath === normalizedPagePath && item.items && Array.isArray(item.items) && item.items.length > 0) {
|
||||
for (const subItem of item.items) {
|
||||
if ('excludeFromIndex' in subItem && subItem.excludeFromIndex) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (subItem.link) {
|
||||
links.push({
|
||||
title: subItem.text || '',
|
||||
link: subItem.link,
|
||||
description: getPageDescription(subItem.link, srcDir)
|
||||
})
|
||||
}
|
||||
}
|
||||
return links
|
||||
}
|
||||
}
|
||||
|
||||
if (item.items && Array.isArray(item.items)) {
|
||||
const found = findItemsRecursive(item.items, pagePath, srcDir)
|
||||
if (found.length > 0) {
|
||||
return found
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return links
|
||||
}
|
||||
|
||||
export const extractItemsForPage = (
|
||||
items: SidebarItem[],
|
||||
pagePath: string,
|
||||
srcDir: string
|
||||
) => findItemsRecursive(items, pagePath, srcDir)
|
||||
@@ -0,0 +1,6 @@
|
||||
export const normalizeLink = (link: string): string =>
|
||||
!link ? '' : link
|
||||
.replace(/^\/+/, '')
|
||||
.replace(/\.md$/, '')
|
||||
.replace(/\\/g, '/')
|
||||
.trim()
|
||||
@@ -0,0 +1,12 @@
|
||||
import { SectionLinkListItem } from "../../theme/components/SectionLinkList/SectionLinkList.types"
|
||||
|
||||
export type SidebarItem = {
|
||||
text: string
|
||||
link?: string
|
||||
items?: SidebarItem[]
|
||||
}
|
||||
|
||||
export type Frontmatter = {
|
||||
section_links?: SectionLinkListItem[]
|
||||
[key: string]: string | number | boolean | string[] | SectionLinkListItem[] | undefined
|
||||
}
|
||||
@@ -2,6 +2,8 @@ export type SectionLinkListItem = {
|
||||
title: string,
|
||||
link: string,
|
||||
description?: string
|
||||
collapsed?: boolean
|
||||
excludeFromIndex?: boolean
|
||||
}
|
||||
|
||||
export type SectionLinkListProps = {
|
||||
|
||||
@@ -7,8 +7,8 @@ section_links:
|
||||
link: /compute/compute-how-to/compute-connect-inside.md
|
||||
description: Подключиться к виртуальной машине по SSH с помощью ключевой пары по внутреннему IP-адресу через джамп-хост
|
||||
- title: Подключение по SSH по логину и паролю
|
||||
link: /compute/compute-how-to/compute-connect-inside.md
|
||||
link: /compute/compute-how-to/compute-connect-pwd.md
|
||||
description: Подключиться к виртуальной машине по SSH с помощью логина и пароля
|
||||
---
|
||||
---
|
||||
|
||||
# Подключение к ВМ
|
||||
# Подключение к ВМ
|
||||
@@ -14,9 +14,9 @@ section_links:
|
||||
description: Резервирование, назначение IP-адреса виртуальной машине, удаление IP-адресов
|
||||
- title: Группы размещения
|
||||
link: /compute/compute-how-to/compute-affinity.md
|
||||
description: Создание правил размещения виртуальных машин на физических хостах, управление группами размещения
|
||||
---
|
||||
description: Создание правил размещения виртуальных машин на физических хостах, управление группами размещения
|
||||
---
|
||||
|
||||
# Виртуальные машины
|
||||
|
||||
Сервис **Виртуальные машины** предоставляет пользователям виртуальные машины.
|
||||
Сервис **Виртуальные машины** предоставляет пользователям виртуальные машины.
|
||||
Reference in New Issue
Block a user